Update submodules

This commit is contained in:
Ilya Laktyushin 2019-04-06 23:50:32 +04:00
parent 40415c8568
commit a9697172b8
21 changed files with 422 additions and 102 deletions

View File

@ -67,7 +67,8 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
let apiId: Int32 = BuildConfig.shared().apiId
let languagesCategory = "ios"
let appGroupName = "group.\(appBundleIdentifier[..<lastDotRange.lowerBound])"
let baseAppBundleId = String(appBundleIdentifier[..<lastDotRange.lowerBound])
let appGroupName = "group.\(baseAppBundleId)"
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
guard let appGroupUrl = maybeAppGroupUrl else {
@ -117,7 +118,10 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
sharedAccountContext = SharedAccountContext(mainWindow: nil, basePath: rootPath, encryptionKey: BuildConfig.encryptionKey(rootPath), accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: BuildConfig.shared().bundleData), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
let encryptionParameters = ValueBoxEncryptionParameters(key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
sharedAccountContext = SharedAccountContext(mainWindow: nil, basePath: rootPath, encryptionParameters: encryptionParameters, accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: BuildConfig.shared().bundleData), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
}
}

View File

@ -6,5 +6,9 @@
<array>
<string>group.org.telegram.TelegramHD</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)org.telegram.TelegramHD</string>
</array>
</dict>
</plist>

View File

@ -6,5 +6,9 @@
<array>
<string>group.ph.telegra.Telegraph</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)ph.telegra.Telegraph</string>
</array>
</dict>
</plist>

View File

@ -6,5 +6,9 @@
<array>
<string>group.org.telegram.Telegram-iOS</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)org.telegram.Telegram-iOS</string>
</array>
</dict>
</plist>

View File

@ -107,7 +107,8 @@ class ShareRootController: UIViewController {
let apiId: Int32 = BuildConfig.shared().apiId
let languagesCategory = "ios"
let appGroupName = "group.\(appBundleIdentifier[..<lastDotRange.lowerBound])"
let baseAppBundleId = String(appBundleIdentifier[..<lastDotRange.lowerBound])
let appGroupName = "group.\(baseAppBundleId)"
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
guard let appGroupUrl = maybeAppGroupUrl else {
@ -160,7 +161,10 @@ class ShareRootController: UIViewController {
})
semaphore.wait()
let sharedContext = SharedAccountContext(mainWindow: nil, basePath: rootPath, encryptionKey: BuildConfig.encryptionKey(rootPath), accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: BuildConfig.shared().bundleData), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
let encryptionParameters = ValueBoxEncryptionParameters(key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
let sharedContext = SharedAccountContext(mainWindow: nil, basePath: rootPath, encryptionParameters: encryptionParameters, accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: BuildConfig.shared().bundleData), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
sharedExtensionContext = SharedExtensionContext(sharedContext: sharedContext)
globalSharedExtensionContext = sharedExtensionContext
}

View File

@ -58,7 +58,8 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
let apiId: Int32 = BuildConfig.shared().apiId
let languagesCategory = "ios"
let appGroupName = "group.\(appBundleIdentifier[..<lastDotRange.lowerBound])"
let baseAppBundleId = String(appBundleIdentifier[..<lastDotRange.lowerBound])
let appGroupName = "group.\(baseAppBundleId)"
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
guard let appGroupUrl = maybeAppGroupUrl else {
@ -84,7 +85,10 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
initializeAccountManagement()
let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata")
account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: BuildConfig.shared().bundleData), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionKey: BuildConfig.encryptionKey(rootPath))
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
let encryptionParameters = ValueBoxEncryptionParameters(key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: BuildConfig.shared().bundleData), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionParameters: encryptionParameters)
|> mapToSignal { account -> Signal<Account, NoError> in
if let account = account {
switch account {

View File

@ -2769,6 +2769,9 @@
com.apple.BackgroundModes = {
enabled = 1;
};
com.apple.Keychain = {
enabled = 1;
};
com.apple.Push = {
enabled = 1;
};
@ -2804,6 +2807,9 @@
com.apple.ApplicationGroups.iOS = {
enabled = 1;
};
com.apple.Keychain = {
enabled = 1;
};
};
};
D0B2F736204F4C9900D3BFB9 = {

View File

@ -13,6 +13,8 @@ import CloudKit
private let handleVoipNotifications = false
private var testIsLaunched = false
private func encodeText(_ string: String, _ key: Int) -> String {
var result = ""
for c in string.unicodeScalars {
@ -200,6 +202,9 @@ final class SharedApplicationContext {
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? = nil) -> Bool {
precondition(!testIsLaunched)
testIsLaunched = true
let statusBarHost = ApplicationStatusBarHost()
let (window, hostView) = nativeWindowHostView()
self.mainWindow = Window1(hostView: hostView, statusBarHost: statusBarHost)
@ -346,7 +351,8 @@ final class SharedApplicationContext {
let networkArguments = NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: PresentationCallManager.voipMaxLayer, appData: BuildConfig.shared().bundleData)
let appGroupName = "group.\(Bundle.main.bundleIdentifier!)"
let baseAppBundleId = Bundle.main.bundleIdentifier!
let appGroupName = "group.\(baseAppBundleId)"
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
guard let appGroupUrl = maybeAppGroupUrl else {
@ -372,7 +378,8 @@ final class SharedApplicationContext {
let rootPath = rootPathForBasePath(appGroupUrl.path)
performAppGroupUpgrades(appGroupPath: appGroupUrl.path, rootPath: rootPath)
let encryptionKey = BuildConfig.encryptionKey(rootPath)
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
let encryptionParameters = ValueBoxEncryptionParameters(key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
TempBox.initializeShared(basePath: rootPath, processType: "app", launchSpecificId: arc4random64())
@ -575,15 +582,15 @@ final class SharedApplicationContext {
let accountManagerSignal = Signal<AccountManager, NoError> { subscriber in
let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata")
return upgradedAccounts(accountManager: accountManager, rootPath: rootPath, encryptionKey: encryptionKey).start(completed: {
return upgradedAccounts(accountManager: accountManager, rootPath: rootPath, encryptionParameters: encryptionParameters).start(completed: {
subscriber.putNext(accountManager)
subscriber.putCompletion()
})
return EmptyDisposable
}
let sharedContextSignal = accountManagerSignal
|> deliverOnMainQueue
|> take(1)
|> mapToSignal { accountManager -> Signal<(SharedApplicationContext, LoggingSettings), NoError> in
var initialPresentationDataAndSettings: InitialPresentationDataAndSettings?
let semaphore = DispatchSemaphore(value: 0)
@ -601,7 +608,7 @@ final class SharedApplicationContext {
let legacyCache = LegacyCache(path: legacyBasePath + "/Caches")
var setPresentationCall: ((PresentationCall?) -> Void)?
let sharedContext = SharedAccountContext(mainWindow: self.mainWindow, basePath: rootPath, encryptionKey: encryptionKey, accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: networkArguments, rootPath: rootPath, legacyBasePath: legacyBasePath, legacyCache: legacyCache, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init), setNotificationCall: { call in
let sharedContext = SharedAccountContext(mainWindow: self.mainWindow, basePath: rootPath, encryptionParameters: encryptionParameters, accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: networkArguments, rootPath: rootPath, legacyBasePath: legacyBasePath, legacyCache: legacyCache, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init), setNotificationCall: { call in
setPresentationCall?(call)
}, navigateToChat: { accountId, peerId, messageId in
self.openChatWhenReady(accountId: accountId, peerId: peerId, messageId: messageId)
@ -720,7 +727,7 @@ final class SharedApplicationContext {
Logger.shared.logToConsole = loggingSettings.logToConsole
Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData
return importedLegacyAccount(basePath: appGroupUrl.path, accountManager: sharedApplicationContext.sharedContext.accountManager, encryptionKey: encryptionKey, present: { controller in
return importedLegacyAccount(basePath: appGroupUrl.path, accountManager: sharedApplicationContext.sharedContext.accountManager, encryptionParameters: encryptionParameters, present: { controller in
self.window?.rootViewController?.present(controller, animated: true, completion: nil)
})
|> `catch` { _ -> Signal<ImportedLegacyAccountEvent, NoError> in

View File

@ -1,5 +1,12 @@
#import <Foundation/Foundation.h>
@interface DeviceSpecificEncryptionParameters : NSObject
@property (nonatomic, strong) NSData * _Nonnull key;
@property (nonatomic, strong) NSData * _Nonnull salt;
@end
@interface BuildConfig : NSObject
+ (instancetype _Nonnull)sharedBuildConfig;
@ -13,6 +20,6 @@
@property (nonatomic, readonly) int64_t appStoreId;
@property (nonatomic, strong, readonly) NSString * _Nonnull appSpecificUrlScheme;
+ (NSData * _Nonnull)encryptionKey:(NSString * _Nonnull)rootPath;
+ (DeviceSpecificEncryptionParameters * _Nonnull)deviceSpecificEncryptionParameters:(NSString * _Nonnull)rootPath baseAppBundleId:(NSString * _Nonnull)baseAppBundleId;
@end

View File

@ -227,6 +227,62 @@ static MTPKCS * _Nullable checkSignature(const char *filename) {
return result;
}
@interface LocalPrivateKey : NSObject {
SecKeyRef _privateKey;
SecKeyRef _publicKey;
}
- (NSData * _Nullable)encrypt:(NSData * _Nonnull)data;
- (NSData * _Nullable)decrypt:(NSData * _Nonnull)data;
@end
@implementation LocalPrivateKey
- (instancetype _Nonnull)initWithPrivateKey:(SecKeyRef)privateKey publicKey:(SecKeyRef)publicKey {
self = [super init];
if (self != nil) {
_privateKey = (SecKeyRef)CFRetain(privateKey);
_publicKey = (SecKeyRef)CFRetain(publicKey);
}
return self;
}
- (void)dealloc {
CFRelease(_privateKey);
CFRelease(_publicKey);
}
- (NSData * _Nullable)encrypt:(NSData * _Nonnull)data {
if (data.length % 16 != 0) {
return nil;
}
CFErrorRef error = NULL;
NSData *cipherText = (NSData *)CFBridgingRelease(SecKeyCreateEncryptedData(_publicKey, kSecKeyAlgorithmECIESEncryptionCofactorX963SHA256AESGCM, (__bridge CFDataRef)data, &error));
if (!cipherText) {
__unused NSError *err = CFBridgingRelease(error);
return nil;
}
return cipherText;
}
- (NSData * _Nullable)decrypt:(NSData * _Nonnull)data {
CFErrorRef error = NULL;
NSData *plainText = (NSData *)CFBridgingRelease(SecKeyCreateDecryptedData(_privateKey, kSecKeyAlgorithmECIESEncryptionCofactorX963SHA256AESGCM, (__bridge CFDataRef)data, &error));
if (!plainText) {
__unused NSError *err = CFBridgingRelease(error);
return nil;
}
return plainText;
}
@end
@interface BuildConfig () {
NSData * _Nullable _bundleData;
int32_t _apiId;
@ -236,6 +292,19 @@ static MTPKCS * _Nullable checkSignature(const char *filename) {
@end
@implementation DeviceSpecificEncryptionParameters
- (instancetype)initWithKey:(NSData * _Nonnull)key salt:(NSData * _Nonnull)salt {
self = [super init];
if (self != nil) {
_key = key;
_salt = salt;
}
return self;
}
@end
@implementation BuildConfig
+ (NSString *)bundleId {
@ -382,17 +451,205 @@ static MTPKCS * _Nullable checkSignature(const char *filename) {
return @(APP_SPECIFIC_URL_SCHEME);
}
+ (NSData * _Nonnull)encryptionKey:(NSString * _Nonnull)rootPath {
NSString *filePath = [rootPath stringByAppendingPathComponent:@".tempkey"];
NSData *data = [NSData dataWithContentsOfFile:filePath];
if (data != nil) {
return data;
+ (NSString * _Nullable)bundleSeedId {
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);
}
NSMutableData *randomData = [[NSMutableData alloc] initWithLength:32];
int result = SecRandomCopyBytes(kSecRandomDefault, randomData.length, [randomData mutableBytes]);
assert(result == 0);
[randomData writeToFile:filePath atomically:false];
return randomData;
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;
}
+ (LocalPrivateKey * _Nullable)getLocalPrivateKey:(NSString * _Nonnull)baseAppBundleId {
NSString *bundleSeedId = [self bundleSeedId];
if (bundleSeedId == nil) {
return nil;
}
NSString *accessGroup = [bundleSeedId stringByAppendingFormat:@".%@", baseAppBundleId];
NSData *applicationTag = [@"telegramLocalKey" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *query = @{
(id)kSecClass: (id)kSecClassKey,
(id)kSecAttrApplicationTag: applicationTag,
(id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
(id)kSecAttrAccessGroup: (id)accessGroup,
(id)kSecReturnRef: @YES,
};
SecKeyRef privateKey = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&privateKey);
if (status != errSecSuccess) {
return nil;
}
SecKeyRef publicKey = SecKeyCopyPublicKey(privateKey);
if (!publicKey) {
if (privateKey) {
CFRelease(privateKey);
}
return nil;
}
LocalPrivateKey *result = [[LocalPrivateKey alloc] initWithPrivateKey:privateKey publicKey:publicKey];
if (publicKey) {
CFRelease(publicKey);
}
if (privateKey) {
CFRelease(privateKey);
}
return result;
}
+ (bool)removeLocalPrivateKey:(NSString * _Nonnull)baseAppBundleId {
NSString *bundleSeedId = [self bundleSeedId];
if (bundleSeedId == nil) {
return nil;
}
NSData *applicationTag = [@"telegramLocalKey" dataUsingEncoding:NSUTF8StringEncoding];
NSString *accessGroup = [bundleSeedId stringByAppendingFormat:@".%@", baseAppBundleId];
NSDictionary *query = @{
(id)kSecClass: (id)kSecClassKey,
(id)kSecAttrApplicationTag: applicationTag,
(id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
(id)kSecAttrAccessGroup: (id)accessGroup
};
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
if (status != errSecSuccess) {
return false;
}
return true;
}
+ (LocalPrivateKey * _Nullable)addLocalPrivateKey:(NSString * _Nonnull)baseAppBundleId {
NSString *bundleSeedId = [self bundleSeedId];
if (bundleSeedId == nil) {
return nil;
}
NSData *applicationTag = [@"telegramLocalKey" dataUsingEncoding:NSUTF8StringEncoding];
NSString *accessGroup = [bundleSeedId stringByAppendingFormat:@".%@", baseAppBundleId];
SecAccessControlRef access = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleAlwaysThisDeviceOnly, kSecAccessControlPrivateKeyUsage, NULL);
NSDictionary *attributes = @{
(id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
(id)kSecAttrKeySizeInBits: @256,
(id)kSecAttrTokenID: (id)kSecAttrTokenIDSecureEnclave,
(id)kSecPrivateKeyAttrs: @{
(id)kSecAttrIsPermanent: @YES,
(id)kSecAttrApplicationTag: applicationTag,
(id)kSecAttrAccessControl: (__bridge id)access,
(id)kSecAttrAccessGroup: (id)accessGroup,
},
};
CFErrorRef error = NULL;
SecKeyRef privateKey = SecKeyCreateRandomKey((__bridge CFDictionaryRef)attributes, &error);
if (!privateKey) {
if (access) {
CFRelease(access);
}
__unused NSError *err = CFBridgingRelease(error);
return nil;
}
SecKeyRef publicKey = SecKeyCopyPublicKey(privateKey);
if (!publicKey) {
if (privateKey) {
CFRelease(privateKey);
}
if (access) {
CFRelease(access);
}
__unused NSError *err = CFBridgingRelease(error);
return nil;
}
LocalPrivateKey *result = [[LocalPrivateKey alloc] initWithPrivateKey:privateKey publicKey:publicKey];
if (publicKey) {
CFRelease(publicKey);
}
if (privateKey) {
CFRelease(privateKey);
}
if (access) {
CFRelease(access);
}
return result;
}
+ (DeviceSpecificEncryptionParameters * _Nonnull)deviceSpecificEncryptionParameters:(NSString * _Nonnull)rootPath baseAppBundleId:(NSString * _Nonnull)baseAppBundleId {
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
NSString *filePath = [rootPath stringByAppendingPathComponent:@".tempkey"];
NSString *encryptedPath = [rootPath stringByAppendingPathComponent:@".tempkeyEncrypted"];
NSData *currentData = [NSData dataWithContentsOfFile:filePath];
NSData *resultData = nil;
if (currentData != nil && currentData.length == 32 + 16) {
resultData = currentData;
}
if (resultData == nil) {
NSMutableData *randomData = [[NSMutableData alloc] initWithLength:32 + 16];
int result = SecRandomCopyBytes(kSecRandomDefault, randomData.length, [randomData mutableBytes]);
if (currentData != nil && currentData.length == 32) { // upgrade key with salt
[currentData getBytes:randomData.mutableBytes length:32];
}
assert(result == 0);
resultData = randomData;
[resultData writeToFile:filePath atomically:false];
}
LocalPrivateKey *localPrivateKey = [self getLocalPrivateKey:baseAppBundleId];
if (localPrivateKey == nil) {
localPrivateKey = [self addLocalPrivateKey:baseAppBundleId];
}
NSData *currentEncryptedData = [NSData dataWithContentsOfFile:encryptedPath];
if (localPrivateKey != nil) {
if (currentEncryptedData != nil) {
NSData *decryptedData = [localPrivateKey decrypt:currentEncryptedData];
if (![resultData isEqualToData:decryptedData]) {
NSData *encryptedData = [localPrivateKey encrypt:resultData];
[encryptedData writeToFile:encryptedPath atomically:false];
assert(false);
}
} else {
NSData *encryptedData = [localPrivateKey encrypt:resultData];
[encryptedData writeToFile:encryptedPath atomically:false];
}
}
CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent();
NSLog(@"deviceSpecificEncryptionParameters took %f ms", (endTime - startTime) * 1000.0);
NSData *key = [resultData subdataWithRange:NSMakeRange(0, 32)];
NSData *salt = [resultData subdataWithRange:NSMakeRange(32, 16)];
return [[DeviceSpecificEncryptionParameters alloc] initWithKey:key salt:salt];
}
@end

View File

@ -106,7 +106,7 @@ enum ImportedLegacyAccountEvent {
case result(AccountRecordId?)
}
func importedLegacyAccount(basePath: String, accountManager: AccountManager, encryptionKey: Data, present: @escaping (UIViewController) -> Void) -> Signal<ImportedLegacyAccountEvent, AccountImportError> {
func importedLegacyAccount(basePath: String, accountManager: AccountManager, encryptionParameters: ValueBoxEncryptionParameters, present: @escaping (UIViewController) -> Void) -> Signal<ImportedLegacyAccountEvent, AccountImportError> {
let queue = Queue()
return deferred { () -> Signal<ImportedLegacyAccountEvent, AccountImportError> in
let documentsPath = basePath + "/Documents"
@ -218,7 +218,7 @@ func importedLegacyAccount(basePath: String, accountManager: AccountManager, enc
}
}
return temporaryAccount(manager: accountManager, rootPath: rootPathForBasePath(basePath), encryptionKey: encryptionKey)
return temporaryAccount(manager: accountManager, rootPath: rootPathForBasePath(basePath), encryptionParameters: encryptionParameters)
|> introduceError(AccountImportError.self)
|> mapToSignal { account -> Signal<ImportedLegacyAccountEvent, AccountImportError> in
let actions = importedAccountData(basePath: basePath, documentsPath: documentsPath, accountManager: accountManager, account: account, database: database)

View File

@ -28,5 +28,9 @@
<array>
<string>group.org.telegram.Telegram-iOS</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)org.telegram.Telegram-iOS</string>
</array>
</dict>
</plist>

View File

@ -1,69 +1,65 @@
// Notifications
"PUSH_MESSAGE_TEXT" = "%1$@: %2$@";
"PUSH_MESSAGE_NOTEXT" = "%1$@ sent you a message";
"PUSH_MESSAGE_PHOTO" = "%1$@ sent you a photo";
"PUSH_MESSAGE_PHOTO_SECRET" = "%1$@ sent you a self-destructing photo";
"PUSH_MESSAGE_VIDEO" = "%1$@ sent you a video";
"PUSH_MESSAGE_VIDEO_SECRET" = "%1$@ sent you a self-destructing video";
"PUSH_MESSAGE_ROUND" = "%1$@ sent you a video message";
"PUSH_MESSAGE_CONTACT" = "%1$@ shared a contact with you";
"PUSH_MESSAGE_GEO" = "%1$@ sent you a map";
"PUSH_MESSAGE_GEOLIVE" = "%1$@ started sharing their live location";
"PUSH_MESSAGE_DOC" = "%1$@ sent you a file";
"PUSH_MESSAGE_AUDIO" = "%1$@ sent you a voice message";
"PUSH_MESSAGE_GIF" = "%1$@ sent you a GIF";
"PUSH_MESSAGE_TEXT" = "%1$@|%2$@";
"PUSH_MESSAGE_NOTEXT" = "%1$@|sent you a message";
"PUSH_MESSAGE_PHOTO" = "%1$@|sent you a photo";
"PUSH_MESSAGE_PHOTO_SECRET" = "%1$@|sent you a self-destructing photo";
"PUSH_MESSAGE_VIDEO" = "%1$@|sent you a video";
"PUSH_MESSAGE_VIDEO_SECRET" = "%1$@|sent you a self-destructing video";
"PUSH_MESSAGE_ROUND" = "%1$@|sent you a video message";
"PUSH_MESSAGE_CONTACT" = "%1$@|shared a contact %2$@ with you";
"PUSH_MESSAGE_GEO" = "%1$@|sent you a map";
"PUSH_MESSAGE_GEOLIVE" = "%1$@|started sharing their live location";
"PUSH_MESSAGE_DOC" = "%1$@|sent you a file";
"PUSH_MESSAGE_AUDIO" = "%1$@|sent you a voice message";
"PUSH_MESSAGE_GIF" = "%1$@|sent you a GIF";
"PUSH_ENCRYPTED_MESSAGE" = "You have a new message%1$@";
"PUSH_LOCKED_MESSAGE" = "You have a new message%1$@";
"PUSH_MESSAGE_SCREENSHOT" = "%1$@ took a screenshot!";
"PUSH_MESSAGE_SCREENSHOT" = "%1$@|took a screenshot!";
"PUSH_ENCRYPTION_REQUEST" = "New encryption request%1$@";
"PUSH_ENCRYPTION_ACCEPT" = "Your encryption request was accepted%1$@";
"PUSH_MESSAGE_POLL" = "%1$@ sent you a poll";
"PUSH_CHANNEL_MESSAGE_POLL" = "%1$@ posted a poll";
"PUSH_CHAT_MESSAGE_POLL" = "%1$@ sent a poll to the group %2$@";
"PUSH_PINNED_POLL" = "%1$@ pinned a poll";
"PUSH_MESSAGE_POLL" = "%1$@|sent you a poll %2$@";
"PUSH_CHANNEL_MESSAGE_POLL" = "%1$@|posted a poll %2$@";
"PUSH_PINNED_POLL" = "%1$@|pinned a poll";
"PUSH_CHAT_MESSAGE_TEXT" = "%1$@@%2$@: %3$@";
"PUSH_CHAT_MESSAGE_NOTEXT" = "%1$@ sent a message to the group %2$@";
"PUSH_CHAT_MESSAGE_PHOTO" = "%1$@ sent a photo to the group %2$@";
"PUSH_CHAT_MESSAGE_VIDEO" = "%1$@ sent a video to the group %2$@";
"PUSH_CHAT_MESSAGE_ROUND" = "%1$@ sent a video message to the group %2$@";
"PUSH_CHAT_MESSAGE_CONTACT" = "%1$@ shared a contact in the group %2$@";
"PUSH_CHAT_MESSAGE_GEO" = "%1$@ sent a map to the group %2$@";
"PUSH_CHAT_MESSAGE_GEOLIVE" = "%1$@ started sharing their live location with %2$@";
"PUSH_CHAT_MESSAGE_DOC" = "%1$@ sent a file to the group %2$@";
"PUSH_CHAT_MESSAGE_AUDIO" = "%1$@ sent a voice message to the group %2$@";
"PUSH_CHAT_MESSAGE_GIF" = "%1$@ sent a GIF to the group %2$@";
"PUSH_CHAT_CREATED" = "%1$@ invited you to the group %2$@";
"PUSH_CHAT_TITLE_EDITED" = "%1$@ edited the group's %2$@ name";
"PUSH_CHAT_PHOTO_EDITED" = "%1$@ edited the group's %2$@ photo";
"PUSH_CHAT_ADD_MEMBER" = "%1$@ invited %3$@ to the group %2$@";
"PUSH_CHAT_ADD_YOU" = "%1$@ invited you to the group %2$@";
"PUSH_CHAT_DELETE_YOU" = "%1$@ removed you from the group %2$@";
"PUSH_CHAT_DELETE_MEMBER" = "%1$@ removed %3$@ from the group %2$@";
"PUSH_CHAT_LEFT" = "%1$@ left the group %2$@";
"PUSH_CHAT_RETURNED" = "%1$@ returned to the group %2$@";
"PUSH_CHAT_MESSAGE_PHOTOS" = "%1$@ sent %3$@ photos to the group %2$@";
"PUSH_CHAT_MESSAGES" = "%1$@ sent %3$@ messages to the group %2$@";
"PUSH_CHAT_MESSAGE_TEXT" = "%2$@|%1$@: %3$@";
"PUSH_CHAT_MESSAGE_NOTEXT" = "%2$@|%1$@ sent a message";
"PUSH_CHAT_MESSAGE_PHOTO" = "%2$@|%1$@ sent a photo";
"PUSH_CHAT_MESSAGE_VIDEO" = "%2$@|%1$@ sent a video";
"PUSH_CHAT_MESSAGE_ROUND" = "%2$@|%1$@ sent a video message";
"PUSH_CHAT_MESSAGE_CONTACT" = "%2$@|%1$@ shared a contact %3$@";
"PUSH_CHAT_MESSAGE_GEO" = "%2$@|%1$@ sent a map";
"PUSH_CHAT_MESSAGE_GEOLIVE" = "%2$@|%1$@ started sharing their live location";
"PUSH_CHAT_MESSAGE_DOC" = "%2$@|%1$@ sent a file";
"PUSH_CHAT_MESSAGE_AUDIO" = "%2$@|%1$@ sent a voice message";
"PUSH_CHAT_MESSAGE_GIF" = "%2$@|%1$@ sent a GIF";
"PUSH_CHAT_CREATED" = "%2$@|%1$@ invited you to the group";
"PUSH_CHAT_TITLE_EDITED" = "%2$@|%1$@ edited the group's name";
"PUSH_CHAT_PHOTO_EDITED" = "%2$@|%1$@ edited the group's photo";
"PUSH_CHAT_ADD_MEMBER" = "%2$@|%1$@ invited %3$@ to the group";
"PUSH_CHAT_ADD_YOU" = "%2$@|%1$@ invited you to the group";
"PUSH_CHAT_DELETE_YOU" = "%2$@|%1$@ removed you from the group";
"PUSH_CHAT_DELETE_MEMBER" = "%2$@|%1$@ removed %3$@ from the group";
"PUSH_CHAT_LEFT" = "%2$@|%1$@ left the group";
"PUSH_CHAT_RETURNED" = "%2$@|%1$@ returned to the group";
"PUSH_MESSAGE_STICKER" = "%1$@ sent you a %2$@sticker";
"PUSH_CHAT_MESSAGE_STICKER" = "%1$@ sent a %3$@sticker to the group %2$@";
"PUSH_MESSAGE_STICKER" = "%1$@|sent you a %2$@sticker";
"PUSH_CHAT_MESSAGE_STICKER" = "%2$@|%1$@ sent a %3$@sticker";
"PUSH_CONTACT_JOINED" = "%1$@ joined Telegram!";
"PUSH_CONTACT_JOINED" = "%1$@|joined Telegram!";
"PUSH_CHANNEL_MESSAGE_TEXT" = "%1$@: %2$@";
"PUSH_CHANNEL_MESSAGE_NOTEXT" = "%1$@ posted a message";
"PUSH_CHANNEL_MESSAGE_PHOTO" = "%1$@ posted a photo";
"PUSH_CHANNEL_MESSAGE_VIDEO" = "%1$@ posted a video";
"PUSH_CHANNEL_MESSAGE_ROUND" = "%1$@ posted a video message";
"PUSH_CHANNEL_MESSAGE_DOC" = "%1$@ posted a document";
"PUSH_CHANNEL_MESSAGE_STICKER" = "%1$@ posted a %2$@sticker";
"PUSH_CHANNEL_MESSAGE_AUDIO" = "%1$@ posted a voice message";
"PUSH_CHANNEL_MESSAGE_CONTACT" = "%1$@ posted a contact";
"PUSH_CHANNEL_MESSAGE_GEO" = "%1$@ posted a map";
"PUSH_CHANNEL_MESSAGE_GEOLIVE" = "%1$@ posted a live location";
"PUSH_CHANNEL_MESSAGE_GIF" = "%1$@ posted a GIF";
"PUSH_CHANNEL_MESSAGES" = "%1$@ posted %2$@ messages";
"PUSH_CHANNEL_MESSAGE_TEXT" = "%1$@|%2$@";
"PUSH_CHANNEL_MESSAGE_NOTEXT" = "%1$@|posted a message";
"PUSH_CHANNEL_MESSAGE_PHOTO" = "%1$@|posted a photo";
"PUSH_CHANNEL_MESSAGE_VIDEO" = "%1$@|posted a video";
"PUSH_CHANNEL_MESSAGE_ROUND" = "%1$@|posted a video message";
"PUSH_CHANNEL_MESSAGE_DOC" = "%1$@|posted a document";
"PUSH_CHANNEL_MESSAGE_STICKER" = "%1$@|posted a %2$@sticker";
"PUSH_CHANNEL_MESSAGE_AUDIO" = "%1$@|posted a voice message";
"PUSH_CHANNEL_MESSAGE_CONTACT" = "%1$@|posted a %2$@ contact";
"PUSH_CHANNEL_MESSAGE_GEO" = "%1$@|posted a map";
"PUSH_CHANNEL_MESSAGE_GEOLIVE" = "%1$@|posted a live location";
"PUSH_CHANNEL_MESSAGE_GIF" = "%1$@|posted a GIF";
"PUSH_PINNED_TEXT" = "%1$@ pinned \"%2$@\" ";
"PUSH_PINNED_NOTEXT" = "%1$@ pinned a message";
@ -73,14 +69,13 @@
"PUSH_PINNED_DOC" = "%1$@ pinned a file";
"PUSH_PINNED_STICKER" = "%1$@ pinned a %2$@sticker";
"PUSH_PINNED_AUDIO" = "%1$@ pinned a voice message";
"PUSH_PINNED_CONTACT" = "%1$@ pinned a contact";
"PUSH_PINNED_GEO" = "%1$@ pinned a map";
"PUSH_PINNED_GEOLIVE" = "%1$@ pinned a live location";
"PUSH_PINNED_GIF" = "%1$@ pinned a GIF";
"PUSH_MESSAGE_GAME" = "%1$@ invited you to play %2$@";
"PUSH_CHANNEL_MESSAGE_GAME" = "%1$@ invited you to play %2$@";
"PUSH_CHAT_MESSAGE_GAME" = "%1$@ invited the group %2$@ to play %3$@";
"PUSH_MESSAGE_GAME" = "%1$@|invited you to play %2$@";
"PUSH_CHANNEL_MESSAGE_GAME" = "%1$@|invited you to play %2$@";
"PUSH_CHAT_MESSAGE_GAME" = "%2$@|%1$@ invited the group to play %3$@";
"PUSH_PINNED_GAME" = "%1$@ pinned a game";
"PUSH_MESSAGE_TEXT" = "%1$@|%2$@";
@ -116,6 +111,7 @@
"PUSH_MESSAGE" = "%1$@|sent you a message";
"PUSH_MESSAGES_1" = "%1$@|sent you a message";
"PUSH_MESSAGES_any" = "%1$@|sent you %2$d messages";
"PUSH_MESSAGE_ALBUM" = "%1$@|sent you an album";
"PUSH_CHANNEL_MESSAGE_TEXT" = "%1$@|%2$@";
"PUSH_CHANNEL_MESSAGE_NOTEXT" = "%1$@|posted a message";
@ -140,16 +136,16 @@
"PUSH_CHANNEL_MESSAGE_VIDEO" = "%1$@|posted a video";
"PUSH_CHANNEL_MESSAGE_VIDEOS_1" = "%1$@|posted a video";
"PUSH_CHANNEL_MESSAGE_VIDEOS_any" = "%1$@|posted %2$d videos";
"PUSH_CHANNEL_MESSAGE_ROUND" = "%1$@|posted a round video";
"PUSH_CHANNEL_MESSAGE_ROUNDS_1" = "%1$@|posted a round video";
"PUSH_CHANNEL_MESSAGE_ROUNDS_any" = "%1$@|posted %2$d round videos";
"PUSH_CHANNEL_MESSAGE_ROUND" = "%1$@|posted a video message";
"PUSH_CHANNEL_MESSAGE_ROUNDS_1" = "%1$@|posted a video message";
"PUSH_CHANNEL_MESSAGE_ROUNDS_any" = "%1$@|posted %2$d video messages";
"PUSH_CHANNEL_MESSAGE" = "%1$@|posted a message";
"PUSH_CHANNEL_MESSAGES_1" = "%1$@|posted a message";
"PUSH_CHANNEL_MESSAGES_any" = "%1$@|posted %2$d messages";
"PUSH_CHANNEL_ALBUM" = "%1$@|posted an album";
"PUSH_CHAT_MESSAGE_TEXT" = "%2$@|%1$@:%3$@";
"PUSH_CHAT_MESSAGE_NOTEXT" = "%2$@|%1$@ sent a message to the group";
"PUSH_CHAT_MESSAGE_PHOTO" = "%2$@|%1$@ sent a photo";
"PUSH_CHAT_MESSAGE_VIDEO" = "%2$@|%1$@ sent a video ";
"PUSH_CHAT_MESSAGE_ROUND" = "%2$@|%1$@ sent a video message";
"PUSH_CHAT_MESSAGE_DOC" = "%2$@|%1$@ sent a file";
@ -158,7 +154,7 @@
"PUSH_CHAT_MESSAGE_CONTACT" = "%2$@|%1$@ shared a contact";
"PUSH_CHAT_MESSAGE_GEO" = "%2$@|%1$@ sent a map";
"PUSH_CHAT_MESSAGE_GEOLIVE" = "%2$@|%1$@ started sharing their live location";
"PUSH_CHAT_MESSAGE_POLL" = "%2$@|%1$@ sent a poll";
"PUSH_CHAT_MESSAGE_POLL" = "%2$@|%1$@ sent a poll %3$@ to the group";
"PUSH_CHAT_MESSAGE_GIF" = "%2$@|%1$@ sent a GIF";
"PUSH_CHAT_MESSAGE_GAME" = "%2$@|%1$@ invited the group to play %3$@";
"PUSH_CHAT_MESSAGE_INVOICE" = "%2$@|%1$@ sent an invoice for %3$@";
@ -187,6 +183,7 @@
"PUSH_CHAT_MESSAGE" = "%2$@|%1$@ sent a message";
"PUSH_CHAT_MESSAGES_1" = "%2$@|%1$@ sent a message";
"PUSH_CHAT_MESSAGES_any" = "%2$@|%1$@ sent %3$d messages";
"PUSH_CHAT_ALBUM" = "%2$@|%1$@ sent an album";
"PUSH_PINNED_TEXT" = "%1$@|pinned \"%2$@\" ";
"PUSH_PINNED_NOTEXT" = "%1$@|pinned a message";
@ -196,10 +193,10 @@
"PUSH_PINNED_DOC" = "%1$@|pinned a file";
"PUSH_PINNED_STICKER" = "%1$@|pinned a %2$@sticker";
"PUSH_PINNED_AUDIO" = "%1$@|pinned a voice message";
"PUSH_PINNED_CONTACT" = "%1$@|pinned a contact";
"PUSH_PINNED_CONTACT" = "%1$@|pinned a %2$@ contact";
"PUSH_PINNED_GEO" = "%1$@|pinned a map";
"PUSH_PINNED_GEOLIVE" = "%1$@|pinned a live location";
"PUSH_PINNED_POLL" = "|%1$@|pinned a poll";
"PUSH_PINNED_POLL" = "|%1$@|pinned a poll %2$@";
"PUSH_PINNED_GAME" = "%1$@|pinned a game";
"PUSH_PINNED_INVOICE" = "%1$@|pinned an invoice";
"PUSH_PINNED_GIF" = "%1$@|pinned a GIF";
@ -212,6 +209,14 @@
"PUSH_PHONE_CALL_REQUEST" = "%1$@|is calling you!";
"PUSH_PHONE_CALL_MISSED" = "%1$@|You missed a call";
"PUSH_MESSAGE_GAME_SCORE" = "%1$@ scored %3$@ in game %2$@";
"PUSH_MESSAGE_VIDEOS" = "%1$@ sent you %2$@ videos";
"PUSH_MESSAGE_CHANNEL_MESSAGE_GAME_SCORE" = "%1$@ scored %3$@ in game %2$@";
"PUSH_CHANNEL_MESSAGE_VIDEOS" = "%1$@ posted %2$@ videos";
"PUSH_PINNED_GAME_SCORE" = "%1$@ pinned a game score";
"PUSH_CHAT_MESSAGE_GAME_SCORE" = "%1$@ scored %4$@ in game %3$@ in the group %2$@";
"PUSH_CHAT_MESSAGE_VIDEOS" = "%1$@ sent %3$@ videos to the group %2$@";
"LOCAL_MESSAGE_FWDS" = "%1$@ forwarded you %2$d messages";
"LOCAL_CHANNEL_MESSAGE_FWDS" = "%1$@ posted %2$d forwarded messages";
"LOCAL_CHAT_MESSAGE_FWDS" = "%1$@ forwarded %2$d messages";
@ -4211,3 +4216,5 @@ Unused sets are archived when you add more.";
"Call.Flip" = "flip";
"Call.End" = "end";
"Call.Speaker" = "speaker";
"MemberSearch.BotSection" = "BOTS";

View File

@ -57,7 +57,8 @@ class TodayViewController: UIViewController, NCWidgetProviding {
let apiId: Int32 = BuildConfig.shared().apiId
let languagesCategory = "ios"
let appGroupName = "group.\(appBundleIdentifier[..<lastDotRange.lowerBound])"
let baseAppBundleId = String(appBundleIdentifier[..<lastDotRange.lowerBound])
let appGroupName = "group.\(baseAppBundleId)"
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
guard let appGroupUrl = maybeAppGroupUrl else {
@ -78,7 +79,11 @@ class TodayViewController: UIViewController, NCWidgetProviding {
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
initializeAccountManagement()
let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata")
account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: BuildConfig.shared().bundleData), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: auxiliaryMethods, encryptionKey: BuildConfig.encryptionKey(rootPath))
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
let encryptionParameters = ValueBoxEncryptionParameters(key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: BuildConfig.shared().bundleData), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: auxiliaryMethods, encryptionParameters: encryptionParameters)
|> mapToSignal { account -> Signal<Account, NoError> in
if let account = account {
switch account {

@ -1 +1 @@
Subproject commit 7d0164259f2aa48516393a43822b5b8ab701d843
Subproject commit d80864dc37cd4ac3aecae93d2d278051fbb8f921

@ -1 +1 @@
Subproject commit 579d7f626427d0cc82209c1b2fc3be7b97eac68c
Subproject commit 91e7d8de3cbf410eef555867edb948f6a107d44c

@ -1 +1 @@
Subproject commit 5e6b155c5fabf22f0229b700f32918e79dcbc6f4
Subproject commit 73d45d3c87aa5837189521f41e4709a5d3780825

@ -1 +1 @@
Subproject commit 796552703ada04403a3f4f8422c5c4696fd9c066
Subproject commit 2deb9c75411eb395d1864da81978da2c3c95f07b

@ -1 +1 @@
Subproject commit 414fc5cd9f8ae41a95af4dc72f205052313e76fd
Subproject commit ab41021ba7e7d73e248b30cd5b4277720311fce7

@ -1 +1 @@
Subproject commit 11eaf77f43214a2dc716b17c91147e87ec8c941f
Subproject commit 8fbcfd0e888a5fbd9cc788f13998d67d63731134

View File

@ -389,6 +389,7 @@ public final class PresentationStrings {
public let primaryComponent: PresentationStringsComponent
public let secondaryComponent: PresentationStringsComponent?
public let baseLanguageCode: String
public let groupingSeparator: String
private let _s: [Int: String]
private let _r: [Int: [(Int, NSRange)]]
@ -490,7 +491,8 @@ public final class PresentationStrings {
"""
public func \(escapedIdentifier(key))(_ value: Int32) -> String {
let form = presentationStringsPluralizationForm(self.lc, value)
return String(format: self._ps[\(id) * \(PluralizationForm.formCount) + Int(form.rawValue)]!, \"\\(value)\")
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[\(id) * \(PluralizationForm.formCount) + Int(form.rawValue)]!, stringValue)
}
"""
@ -503,9 +505,10 @@ public final class PresentationStrings {
result +=
"""
init(primaryComponent: PresentationStringsComponent, secondaryComponent: PresentationStringsComponent?) {
init(primaryComponent: PresentationStringsComponent, secondaryComponent: PresentationStringsComponent?, groupingSeparator: String) {
self.primaryComponent = primaryComponent
self.secondaryComponent = secondaryComponent
self.groupingSeparator = groupingSeparator
self.baseLanguageCode = secondaryComponent?.languageCode ?? primaryComponent.languageCode