mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Refactoring
This commit is contained in:
parent
0dd4f9d438
commit
47fb45e08f
@ -1,31 +0,0 @@
|
|||||||
|
|
||||||
objc_library(
|
|
||||||
name = "NotificationServiceObjC",
|
|
||||||
enable_modules = True,
|
|
||||||
module_name = "NotificationServiceObjC",
|
|
||||||
srcs = glob([
|
|
||||||
"Sources/**/*.m",
|
|
||||||
"Sources/**/*.h",
|
|
||||||
]),
|
|
||||||
hdrs = glob([
|
|
||||||
"PublicHeaders/**/*.h",
|
|
||||||
]),
|
|
||||||
includes = [
|
|
||||||
"PublicHeaders",
|
|
||||||
],
|
|
||||||
deps = [
|
|
||||||
"//submodules/BuildConfig:BuildConfig",
|
|
||||||
"//submodules/MtProtoKit:MtProtoKit",
|
|
||||||
"//submodules/NotificationsPresentationData:NotificationsPresentationData",
|
|
||||||
"//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider",
|
|
||||||
],
|
|
||||||
sdk_frameworks = [
|
|
||||||
"Foundation",
|
|
||||||
],
|
|
||||||
weak_sdk_frameworks = [
|
|
||||||
"BackgroundTasks",
|
|
||||||
],
|
|
||||||
visibility = [
|
|
||||||
"//visibility:public",
|
|
||||||
],
|
|
||||||
)
|
|
@ -1,17 +0,0 @@
|
|||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <UserNotifications/UserNotifications.h>
|
|
||||||
#import <BuildConfig/BuildConfig.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface NotificationServiceImpl : NSObject
|
|
||||||
|
|
||||||
- (instancetype)initWithSerialDispatch:(void (^)(dispatch_block_t))serialDispatch countIncomingMessage:(void (^)(NSString *, int64_t, DeviceSpecificEncryptionParameters *, int64_t, int32_t))countIncomingMessage isLocked:(bool (^)(NSString *))isLocked lockedMessageText:(NSString *(^)(NSString *))lockedMessageText;
|
|
||||||
|
|
||||||
- (void)updateUnreadCount:(int32_t)unreadCount;
|
|
||||||
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler;
|
|
||||||
- (void)serviceExtensionTimeWillExpire;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -1,308 +0,0 @@
|
|||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Layer 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
@class Api1_Photo;
|
|
||||||
@class Api1_Photo_photoEmpty;
|
|
||||||
@class Api1_Photo_photo;
|
|
||||||
|
|
||||||
@class Api1_PhotoSize;
|
|
||||||
@class Api1_PhotoSize_photoSizeEmpty;
|
|
||||||
@class Api1_PhotoSize_photoSize;
|
|
||||||
@class Api1_PhotoSize_photoCachedSize;
|
|
||||||
@class Api1_PhotoSize_photoStrippedSize;
|
|
||||||
@class Api1_PhotoSize_photoSizeProgressive;
|
|
||||||
|
|
||||||
@class Api1_FileLocation;
|
|
||||||
@class Api1_FileLocation_fileLocationToBeDeprecated;
|
|
||||||
|
|
||||||
@class Api1_DocumentAttribute;
|
|
||||||
@class Api1_DocumentAttribute_documentAttributeImageSize;
|
|
||||||
@class Api1_DocumentAttribute_documentAttributeAnimated;
|
|
||||||
@class Api1_DocumentAttribute_documentAttributeSticker;
|
|
||||||
@class Api1_DocumentAttribute_documentAttributeVideo;
|
|
||||||
@class Api1_DocumentAttribute_documentAttributeAudio;
|
|
||||||
@class Api1_DocumentAttribute_documentAttributeFilename;
|
|
||||||
@class Api1_DocumentAttribute_documentAttributeHasStickers;
|
|
||||||
|
|
||||||
@class Api1_InputStickerSet;
|
|
||||||
@class Api1_InputStickerSet_inputStickerSetEmpty;
|
|
||||||
@class Api1_InputStickerSet_inputStickerSetID;
|
|
||||||
@class Api1_InputStickerSet_inputStickerSetShortName;
|
|
||||||
|
|
||||||
@class Api1_InputFileLocation;
|
|
||||||
@class Api1_InputFileLocation_inputPhotoFileLocation;
|
|
||||||
@class Api1_InputFileLocation_inputDocumentFileLocation;
|
|
||||||
|
|
||||||
@class Api1_MaskCoords;
|
|
||||||
@class Api1_MaskCoords_maskCoords;
|
|
||||||
|
|
||||||
@class Api1_Document;
|
|
||||||
@class Api1_Document_document;
|
|
||||||
|
|
||||||
|
|
||||||
@interface Api1__Environment : NSObject
|
|
||||||
|
|
||||||
+ (NSData *)serializeObject:(id)object;
|
|
||||||
+ (id)parseObject:(NSData *)data;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_FunctionContext : NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSData *payload;
|
|
||||||
@property (nonatomic, copy, readonly) id (^responseParser)(NSData *);
|
|
||||||
@property (nonatomic, strong, readonly) id metadata;
|
|
||||||
|
|
||||||
- (instancetype)initWithPayload:(NSData *)payload responseParser:(id (^)(NSData *))responseParser metadata:(id)metadata;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Types 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
@interface Api1_Photo : NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * pid;
|
|
||||||
|
|
||||||
+ (Api1_Photo_photoEmpty *)photoEmptyWithPid:(NSNumber *)pid;
|
|
||||||
+ (Api1_Photo_photo *)photoWithFlags:(NSNumber *)flags pid:(NSNumber *)pid accessHash:(NSNumber *)accessHash fileReference:(NSData *)fileReference date:(NSNumber *)date sizes:(NSArray *)sizes dcId:(NSNumber *)dcId;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_Photo_photoEmpty : Api1_Photo
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_Photo_photo : Api1_Photo
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * flags;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * accessHash;
|
|
||||||
@property (nonatomic, strong, readonly) NSData * fileReference;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * date;
|
|
||||||
@property (nonatomic, strong, readonly) NSArray * sizes;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * dcId;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@interface Api1_PhotoSize : NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSString * type;
|
|
||||||
|
|
||||||
+ (Api1_PhotoSize_photoSizeEmpty *)photoSizeEmptyWithType:(NSString *)type;
|
|
||||||
+ (Api1_PhotoSize_photoSize *)photoSizeWithType:(NSString *)type location:(Api1_FileLocation *)location w:(NSNumber *)w h:(NSNumber *)h size:(NSNumber *)size;
|
|
||||||
+ (Api1_PhotoSize_photoCachedSize *)photoCachedSizeWithType:(NSString *)type location:(Api1_FileLocation *)location w:(NSNumber *)w h:(NSNumber *)h bytes:(NSData *)bytes;
|
|
||||||
+ (Api1_PhotoSize_photoStrippedSize *)photoStrippedSizeWithType:(NSString *)type bytes:(NSData *)bytes;
|
|
||||||
+ (Api1_PhotoSize_photoSizeProgressive *)photoSizeProgressiveWithType:(NSString *)type location:(Api1_FileLocation *)location w:(NSNumber *)w h:(NSNumber *)h sizes:(NSArray *)sizes;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_PhotoSize_photoSizeEmpty : Api1_PhotoSize
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_PhotoSize_photoSize : Api1_PhotoSize
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) Api1_FileLocation * location;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * w;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * h;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * size;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_PhotoSize_photoCachedSize : Api1_PhotoSize
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) Api1_FileLocation * location;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * w;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * h;
|
|
||||||
@property (nonatomic, strong, readonly) NSData * bytes;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_PhotoSize_photoStrippedSize : Api1_PhotoSize
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSData * bytes;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_PhotoSize_photoSizeProgressive : Api1_PhotoSize
|
|
||||||
|
|
||||||
@property (nonatomic, strong) Api1_FileLocation * location;
|
|
||||||
@property (nonatomic, strong) NSNumber * w;
|
|
||||||
@property (nonatomic, strong) NSNumber * h;
|
|
||||||
@property (nonatomic, strong) NSArray * sizes;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_FileLocation : NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * volumeId;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * localId;
|
|
||||||
|
|
||||||
+ (Api1_FileLocation_fileLocationToBeDeprecated *)fileLocationToBeDeprecatedWithVolumeId:(NSNumber *)volumeId localId:(NSNumber *)localId;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_FileLocation_fileLocationToBeDeprecated : Api1_FileLocation
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@interface Api1_DocumentAttribute : NSObject
|
|
||||||
|
|
||||||
+ (Api1_DocumentAttribute_documentAttributeImageSize *)documentAttributeImageSizeWithW:(NSNumber *)w h:(NSNumber *)h;
|
|
||||||
+ (Api1_DocumentAttribute_documentAttributeAnimated *)documentAttributeAnimated;
|
|
||||||
+ (Api1_DocumentAttribute_documentAttributeSticker *)documentAttributeStickerWithFlags:(NSNumber *)flags alt:(NSString *)alt stickerset:(Api1_InputStickerSet *)stickerset maskCoords:(Api1_MaskCoords *)maskCoords;
|
|
||||||
+ (Api1_DocumentAttribute_documentAttributeVideo *)documentAttributeVideoWithFlags:(NSNumber *)flags duration:(NSNumber *)duration w:(NSNumber *)w h:(NSNumber *)h;
|
|
||||||
+ (Api1_DocumentAttribute_documentAttributeAudio *)documentAttributeAudioWithFlags:(NSNumber *)flags duration:(NSNumber *)duration title:(NSString *)title performer:(NSString *)performer waveform:(NSData *)waveform;
|
|
||||||
+ (Api1_DocumentAttribute_documentAttributeFilename *)documentAttributeFilenameWithFileName:(NSString *)fileName;
|
|
||||||
+ (Api1_DocumentAttribute_documentAttributeHasStickers *)documentAttributeHasStickers;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_DocumentAttribute_documentAttributeImageSize : Api1_DocumentAttribute
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * w;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * h;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_DocumentAttribute_documentAttributeAnimated : Api1_DocumentAttribute
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_DocumentAttribute_documentAttributeSticker : Api1_DocumentAttribute
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * flags;
|
|
||||||
@property (nonatomic, strong, readonly) NSString * alt;
|
|
||||||
@property (nonatomic, strong, readonly) Api1_InputStickerSet * stickerset;
|
|
||||||
@property (nonatomic, strong, readonly) Api1_MaskCoords * maskCoords;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_DocumentAttribute_documentAttributeVideo : Api1_DocumentAttribute
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * flags;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * duration;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * w;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * h;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_DocumentAttribute_documentAttributeAudio : Api1_DocumentAttribute
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * flags;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * duration;
|
|
||||||
@property (nonatomic, strong, readonly) NSString * title;
|
|
||||||
@property (nonatomic, strong, readonly) NSString * performer;
|
|
||||||
@property (nonatomic, strong, readonly) NSData * waveform;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_DocumentAttribute_documentAttributeFilename : Api1_DocumentAttribute
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSString * fileName;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_DocumentAttribute_documentAttributeHasStickers : Api1_DocumentAttribute
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@interface Api1_InputStickerSet : NSObject
|
|
||||||
|
|
||||||
+ (Api1_InputStickerSet_inputStickerSetEmpty *)inputStickerSetEmpty;
|
|
||||||
+ (Api1_InputStickerSet_inputStickerSetID *)inputStickerSetIDWithPid:(NSNumber *)pid accessHash:(NSNumber *)accessHash;
|
|
||||||
+ (Api1_InputStickerSet_inputStickerSetShortName *)inputStickerSetShortNameWithShortName:(NSString *)shortName;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_InputStickerSet_inputStickerSetEmpty : Api1_InputStickerSet
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_InputStickerSet_inputStickerSetID : Api1_InputStickerSet
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * pid;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * accessHash;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_InputStickerSet_inputStickerSetShortName : Api1_InputStickerSet
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSString * shortName;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@interface Api1_InputFileLocation : NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * pid;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * accessHash;
|
|
||||||
@property (nonatomic, strong, readonly) NSData * fileReference;
|
|
||||||
@property (nonatomic, strong, readonly) NSString * thumbSize;
|
|
||||||
|
|
||||||
+ (Api1_InputFileLocation_inputPhotoFileLocation *)inputPhotoFileLocationWithPid:(NSNumber *)pid accessHash:(NSNumber *)accessHash fileReference:(NSData *)fileReference thumbSize:(NSString *)thumbSize;
|
|
||||||
+ (Api1_InputFileLocation_inputDocumentFileLocation *)inputDocumentFileLocationWithPid:(NSNumber *)pid accessHash:(NSNumber *)accessHash fileReference:(NSData *)fileReference thumbSize:(NSString *)thumbSize;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_InputFileLocation_inputPhotoFileLocation : Api1_InputFileLocation
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_InputFileLocation_inputDocumentFileLocation : Api1_InputFileLocation
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@interface Api1_MaskCoords : NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * n;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * x;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * y;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * zoom;
|
|
||||||
|
|
||||||
+ (Api1_MaskCoords_maskCoords *)maskCoordsWithN:(NSNumber *)n x:(NSNumber *)x y:(NSNumber *)y zoom:(NSNumber *)zoom;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_MaskCoords_maskCoords : Api1_MaskCoords
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@interface Api1_Document : NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * flags;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * pid;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * accessHash;
|
|
||||||
@property (nonatomic, strong, readonly) NSData * fileReference;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * date;
|
|
||||||
@property (nonatomic, strong, readonly) NSString * mimeType;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * size;
|
|
||||||
@property (nonatomic, strong, readonly) NSArray * thumbs;
|
|
||||||
@property (nonatomic, strong, readonly) NSNumber * dcId;
|
|
||||||
@property (nonatomic, strong, readonly) NSArray * attributes;
|
|
||||||
|
|
||||||
+ (Api1_Document_document *)documentWithFlags:(NSNumber *)flags pid:(NSNumber *)pid accessHash:(NSNumber *)accessHash fileReference:(NSData *)fileReference date:(NSNumber *)date mimeType:(NSString *)mimeType size:(NSNumber *)size thumbs:(NSArray *)thumbs dcId:(NSNumber *)dcId attributes:(NSArray *)attributes;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface Api1_Document_document : Api1_Document
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Functions 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
@interface Api1: NSObject
|
|
||||||
|
|
||||||
@end
|
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +0,0 @@
|
|||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
id _Nullable parseAttachment(NSData * _Nonnull data);
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -1,31 +0,0 @@
|
|||||||
#import "Attachments.h"
|
|
||||||
|
|
||||||
#import <MTProtoKit/MTProtoKit.h>
|
|
||||||
|
|
||||||
#import "Api.h"
|
|
||||||
|
|
||||||
id _Nullable parseAttachment(NSData * _Nonnull data) {
|
|
||||||
if (data.length < 4) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
MTInputStream *inputStream = [[MTInputStream alloc] initWithData:data];
|
|
||||||
|
|
||||||
int32_t signature = [inputStream readInt32];
|
|
||||||
|
|
||||||
NSData *dataToParse = nil;
|
|
||||||
if (signature == 0x3072cfa1) {
|
|
||||||
NSData *bytes = [inputStream readBytes];
|
|
||||||
if (bytes != nil) {
|
|
||||||
dataToParse = [MTGzip decompress:bytes];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dataToParse = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataToParse == nil) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [Api1__Environment parseObject:dataToParse];
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
#import "StoredAccountInfos.h"
|
|
||||||
#import "Api.h"
|
|
||||||
#import <BuildConfig/BuildConfig.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
dispatch_block_t fetchImage(BuildConfig *buildConfig, AccountProxyConnection * _Nullable proxyConnection, StoredAccountInfo *account, Api1_InputFileLocation *inputFileLocation, int32_t datacenterId, void (^_completion)(NSData * _Nullable));
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -1,209 +0,0 @@
|
|||||||
#import "FetchImage.h"
|
|
||||||
|
|
||||||
#import <MtProtoKit/MtProtoKit.h>
|
|
||||||
#import <OpenSSLEncryptionProvider/OpenSSLEncryptionProvider.h>
|
|
||||||
|
|
||||||
#import "Serialization.h"
|
|
||||||
|
|
||||||
@interface InMemoryKeychain : NSObject <MTKeychain> {
|
|
||||||
NSMutableDictionary *_dict;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation InMemoryKeychain
|
|
||||||
|
|
||||||
- (instancetype)init {
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil) {
|
|
||||||
_dict = [[NSMutableDictionary alloc] init];
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setObject:(id)object forKey:(NSString *)aKey group:(NSString *)group {
|
|
||||||
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object];
|
|
||||||
_dict[[NSString stringWithFormat:@"%@:%@", group, aKey]] = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)objectForKey:(NSString *)aKey group:(NSString *)group {
|
|
||||||
NSData *data = _dict[[NSString stringWithFormat:@"%@:%@", group, aKey]];
|
|
||||||
if (data != nil) {
|
|
||||||
return [NSKeyedUnarchiver unarchiveObjectWithData:data];
|
|
||||||
} else {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeObjectForKey:(NSString *)aKey group:(NSString *)group {
|
|
||||||
[_dict removeObjectForKey:[NSString stringWithFormat:@"%@:%@", group, aKey]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dropGroup:(NSString *)group {
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
static void MTLoggingFunction(NSString *string, va_list args) {
|
|
||||||
NSLogv(string, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@interface ParsedFile : NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSData * _Nullable data;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation ParsedFile
|
|
||||||
|
|
||||||
- (instancetype)initWithData:(NSData * _Nullable)data {
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil) {
|
|
||||||
_data = data;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
dispatch_block_t fetchImage(BuildConfig *buildConfig, AccountProxyConnection * _Nullable proxyConnection, StoredAccountInfo *account, Api1_InputFileLocation *inputFileLocation, int32_t datacenterId, void (^completion)(NSData * _Nullable)) {
|
|
||||||
MTLogSetEnabled(true);
|
|
||||||
MTLogSetLoggingFunction(&MTLoggingFunction);
|
|
||||||
|
|
||||||
Serialization *serialization = [[Serialization alloc] init];
|
|
||||||
|
|
||||||
MTApiEnvironment *apiEnvironment = [[MTApiEnvironment alloc] init];
|
|
||||||
|
|
||||||
apiEnvironment.apiId = buildConfig.apiId;
|
|
||||||
apiEnvironment.langPack = @"ios";
|
|
||||||
apiEnvironment.layer = @([serialization currentLayer]);
|
|
||||||
apiEnvironment.disableUpdates = true;
|
|
||||||
apiEnvironment = [apiEnvironment withUpdatedLangPackCode:@""];
|
|
||||||
|
|
||||||
if (proxyConnection != nil) {
|
|
||||||
apiEnvironment = [apiEnvironment withUpdatedSocksProxySettings:[[MTSocksProxySettings alloc] initWithIp:proxyConnection.host port:(uint16_t)proxyConnection.port username:proxyConnection.username password:proxyConnection.password secret:proxyConnection.secret]];
|
|
||||||
}
|
|
||||||
|
|
||||||
MTContext *context = [[MTContext alloc] initWithSerialization:serialization encryptionProvider:[[OpenSSLEncryptionProvider alloc] init] apiEnvironment:apiEnvironment isTestingEnvironment:account.isTestingEnvironment useTempAuthKeys:true];
|
|
||||||
context.tempKeyExpiration = 10 * 60 * 60;
|
|
||||||
|
|
||||||
NSDictionary *seedAddressList = @{};
|
|
||||||
|
|
||||||
if (account.isTestingEnvironment) {
|
|
||||||
seedAddressList = @{
|
|
||||||
@(1): @[@"149.154.175.10"],
|
|
||||||
@(2): @[@"149.154.167.40"]
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
seedAddressList = @{
|
|
||||||
@(1): @[@"149.154.175.50", @"2001:b28:f23d:f001::a"],
|
|
||||||
@(2): @[@"149.154.167.50", @"2001:67c:4e8:f002::a"],
|
|
||||||
@(3): @[@"149.154.175.100", @"2001:b28:f23d:f003::a"],
|
|
||||||
@(4): @[@"149.154.167.91", @"2001:67c:4e8:f004::a"],
|
|
||||||
@(5): @[@"149.154.171.5", @"2001:b28:f23f:f005::a"]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for (NSNumber *datacenterId in seedAddressList) {
|
|
||||||
NSMutableArray *addressList = [[NSMutableArray alloc] init];
|
|
||||||
for (NSString *host in seedAddressList[datacenterId]) {
|
|
||||||
[addressList addObject:[[MTDatacenterAddress alloc] initWithIp:host port:443 preferForMedia:false restrictToTcp:false cdn:false preferForProxy:false secret:nil]];
|
|
||||||
}
|
|
||||||
[context setSeedAddressSetForDatacenterWithId:[datacenterId intValue] seedAddressSet:[[MTDatacenterAddressSet alloc] initWithAddressList:addressList]];
|
|
||||||
}
|
|
||||||
|
|
||||||
InMemoryKeychain *keychain = [[InMemoryKeychain alloc] init];
|
|
||||||
context.keychain = keychain;
|
|
||||||
|
|
||||||
[context performBatchUpdates:^{
|
|
||||||
for (NSNumber *datacenterId in account.datacenters) {
|
|
||||||
AccountDatacenterInfo *info = account.datacenters[datacenterId];
|
|
||||||
if (info.addressList.count != 0) {
|
|
||||||
NSMutableArray *list = [[NSMutableArray alloc] init];
|
|
||||||
for (AccountDatacenterAddress *address in info.addressList) {
|
|
||||||
[list addObject:[[MTDatacenterAddress alloc] initWithIp:address.host port:address.port preferForMedia:address.isMedia restrictToTcp:false cdn:false preferForProxy:address.isProxy secret:address.secret]];
|
|
||||||
}
|
|
||||||
[context updateAddressSetForDatacenterWithId:[datacenterId intValue] addressSet:[[MTDatacenterAddressSet alloc] initWithAddressList:list] forceUpdateSchemes:true];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
for (NSNumber *datacenterId in account.datacenters) {
|
|
||||||
AccountDatacenterInfo *info = account.datacenters[datacenterId];
|
|
||||||
MTDatacenterAuthInfo *authInfo = [[MTDatacenterAuthInfo alloc] initWithAuthKey:info.masterKey.data authKeyId:info.masterKey.keyId saltSet:@[] authKeyAttributes:@{}];
|
|
||||||
|
|
||||||
[context updateAuthInfoForDatacenterWithId:[datacenterId intValue] authInfo:authInfo selector:MTDatacenterAuthInfoSelectorPersistent];
|
|
||||||
|
|
||||||
if (info.ephemeralMainKey != nil) {
|
|
||||||
MTDatacenterAuthInfo *ephemeralMainAuthInfo = [[MTDatacenterAuthInfo alloc] initWithAuthKey:info.ephemeralMainKey.data authKeyId:info.ephemeralMainKey.keyId saltSet:@[] authKeyAttributes:@{}];
|
|
||||||
[context updateAuthInfoForDatacenterWithId:[datacenterId intValue] authInfo:ephemeralMainAuthInfo selector:MTDatacenterAuthInfoSelectorEphemeralMain];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.ephemeralMediaKey != nil) {
|
|
||||||
MTDatacenterAuthInfo *ephemeralMediaAuthInfo = [[MTDatacenterAuthInfo alloc] initWithAuthKey:info.ephemeralMediaKey.data authKeyId:info.ephemeralMediaKey.keyId saltSet:@[] authKeyAttributes:@{}];
|
|
||||||
[context updateAuthInfoForDatacenterWithId:[datacenterId intValue] authInfo:ephemeralMediaAuthInfo selector:MTDatacenterAuthInfoSelectorEphemeralMedia];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MTProto *mtProto = [[MTProto alloc] initWithContext:context datacenterId:datacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
|
|
||||||
mtProto.useTempAuthKeys = context.useTempAuthKeys;
|
|
||||||
mtProto.checkForProxyConnectionIssues = false;
|
|
||||||
|
|
||||||
MTRequestMessageService *requestService = [[MTRequestMessageService alloc] initWithContext:context];
|
|
||||||
[mtProto addMessageService:requestService];
|
|
||||||
|
|
||||||
MTRequest *request = [[MTRequest alloc] init];
|
|
||||||
|
|
||||||
MTOutputStream *outputStream = [[MTOutputStream alloc] init];
|
|
||||||
[outputStream writeInt32:-475607115]; //upload.getFile
|
|
||||||
[outputStream writeData:[Api1__Environment serializeObject:inputFileLocation]];
|
|
||||||
|
|
||||||
[outputStream writeInt32:0];
|
|
||||||
[outputStream writeInt32:32 * 1024];
|
|
||||||
|
|
||||||
[request setPayload:[outputStream currentBytes] metadata:@"getFile" shortMetadata:@"getFile" responseParser:^id(NSData *response) {
|
|
||||||
MTInputStream *inputStream = [[MTInputStream alloc] initWithData:response];
|
|
||||||
int32_t signature = [inputStream readInt32];
|
|
||||||
if (signature != 157948117) {
|
|
||||||
return [[ParsedFile alloc] initWithData:nil];
|
|
||||||
}
|
|
||||||
[inputStream readInt32]; //type
|
|
||||||
[inputStream readInt32]; //mtime
|
|
||||||
|
|
||||||
return [[ParsedFile alloc] initWithData:[inputStream readBytes]];
|
|
||||||
}];
|
|
||||||
|
|
||||||
request.dependsOnPasswordEntry = false;
|
|
||||||
request.shouldContinueExecutionWithErrorContext = ^bool (__unused MTRequestErrorContext *errorContext) {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
request.completed = ^(id boxedResponse, __unused NSTimeInterval completionTimestamp, MTRpcError *error) {
|
|
||||||
if (error != nil) {
|
|
||||||
if (completion) {
|
|
||||||
completion(nil);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ([boxedResponse isKindOfClass:[ParsedFile class]]) {
|
|
||||||
if (completion) {
|
|
||||||
completion(((ParsedFile *)boxedResponse).data);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (completion) {
|
|
||||||
completion(nil);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[requestService addRequest:request];
|
|
||||||
[mtProto resume];
|
|
||||||
|
|
||||||
id internalId = request.internalId;
|
|
||||||
return ^{
|
|
||||||
[requestService removeRequestByInternalId:internalId];
|
|
||||||
[context performBatchUpdates:^{
|
|
||||||
}];
|
|
||||||
[mtProto stop];
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,494 +0,0 @@
|
|||||||
#import <NotificationServiceObjC/NotificationServiceObjC.h>
|
|
||||||
|
|
||||||
#import <mach/mach.h>
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import <BuildConfig/BuildConfig.h>
|
|
||||||
|
|
||||||
#ifdef __IPHONE_13_0
|
|
||||||
#import <BackgroundTasks/BackgroundTasks.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#import "StoredAccountInfos.h"
|
|
||||||
#import "Attachments.h"
|
|
||||||
#import "Api.h"
|
|
||||||
#import "FetchImage.h"
|
|
||||||
|
|
||||||
static NSData * _Nullable parseBase64(NSString *string) {
|
|
||||||
string = [string stringByReplacingOccurrencesOfString:@"-" withString:@"+"];
|
|
||||||
string = [string stringByReplacingOccurrencesOfString:@"_" withString:@"/"];
|
|
||||||
while (string.length % 4 != 0) {
|
|
||||||
string = [string stringByAppendingString:@"="];
|
|
||||||
}
|
|
||||||
return [[NSData alloc] initWithBase64EncodedString:string options:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
PeerNamespaceCloudUser = 0,
|
|
||||||
PeerNamespaceCloudGroup = 1,
|
|
||||||
PeerNamespaceCloudChannel = 2,
|
|
||||||
PeerNamespaceSecretChat = 3
|
|
||||||
} PeerNamespace;
|
|
||||||
|
|
||||||
static int64_t makePeerId(int32_t namespace, int32_t value) {
|
|
||||||
return (((int64_t)(namespace)) << 32) | ((int64_t)((uint64_t)((uint32_t)value)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
static void reportMemory() {
|
|
||||||
struct task_basic_info info;
|
|
||||||
mach_msg_type_number_t size = TASK_BASIC_INFO_COUNT;
|
|
||||||
kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
|
|
||||||
if (kerr == KERN_SUCCESS) {
|
|
||||||
NSLog(@"Memory in use (in bytes): %lu", info.resident_size);
|
|
||||||
NSLog(@"Memory in use (in MiB): %f", ((CGFloat)info.resident_size / 1048576));
|
|
||||||
} else {
|
|
||||||
NSLog(@"Error with task_info(): %s", mach_error_string(kerr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@interface NotificationServiceImpl () {
|
|
||||||
void (^_serialDispatch)(dispatch_block_t);
|
|
||||||
void (^_countIncomingMessage)(NSString *, int64_t, DeviceSpecificEncryptionParameters *, int64_t, int32_t);
|
|
||||||
|
|
||||||
NSString * _Nullable _rootPath;
|
|
||||||
DeviceSpecificEncryptionParameters * _Nullable _deviceSpecificEncryptionParameters;
|
|
||||||
bool _isLockedValue;
|
|
||||||
NSString *_lockedMessageTextValue;
|
|
||||||
NSString * _Nullable _baseAppBundleId;
|
|
||||||
void (^_contentHandler)(UNNotificationContent *);
|
|
||||||
UNMutableNotificationContent * _Nullable _bestAttemptContent;
|
|
||||||
void (^_cancelFetch)(void);
|
|
||||||
|
|
||||||
NSNumber * _Nullable _updatedUnreadCount;
|
|
||||||
bool _contentReady;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation NotificationServiceImpl
|
|
||||||
|
|
||||||
- (instancetype)initWithSerialDispatch:(void (^)(dispatch_block_t))serialDispatch countIncomingMessage:(void (^)(NSString *, int64_t, DeviceSpecificEncryptionParameters *, int64_t, int32_t))countIncomingMessage isLocked:(nonnull bool (^)(NSString * _Nonnull))isLocked lockedMessageText:(NSString *(^)(NSString *))lockedMessageText {
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil) {
|
|
||||||
#if DEBUG
|
|
||||||
reportMemory();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_serialDispatch = [serialDispatch copy];
|
|
||||||
_countIncomingMessage = [countIncomingMessage copy];
|
|
||||||
|
|
||||||
NSString *appBundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
|
|
||||||
NSRange lastDotRange = [appBundleIdentifier rangeOfString:@"." options:NSBackwardsSearch];
|
|
||||||
if (lastDotRange.location != NSNotFound) {
|
|
||||||
_baseAppBundleId = [appBundleIdentifier substringToIndex:lastDotRange.location];
|
|
||||||
NSString *appGroupName = [@"group." stringByAppendingString:_baseAppBundleId];
|
|
||||||
NSURL *appGroupUrl = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupName];
|
|
||||||
|
|
||||||
if (appGroupUrl != nil) {
|
|
||||||
NSString *rootPath = [[appGroupUrl path] stringByAppendingPathComponent:@"telegram-data"];
|
|
||||||
_rootPath = rootPath;
|
|
||||||
if (rootPath != nil) {
|
|
||||||
_deviceSpecificEncryptionParameters = [BuildConfig deviceSpecificEncryptionParameters:rootPath baseAppBundleId:_baseAppBundleId];
|
|
||||||
|
|
||||||
_isLockedValue = isLocked(rootPath);
|
|
||||||
if (_isLockedValue) {
|
|
||||||
_lockedMessageTextValue = lockedMessageText(rootPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
NSAssert(false, @"appGroupUrl == nil");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
NSAssert(false, @"Invalid bundle id");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)completeWithBestAttemptContent {
|
|
||||||
_contentReady = true;
|
|
||||||
_updatedUnreadCount = @(-1);
|
|
||||||
if (_contentReady && _updatedUnreadCount) {
|
|
||||||
[self _internalComplete];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)updateUnreadCount:(int32_t)unreadCount {
|
|
||||||
_updatedUnreadCount = @(unreadCount);
|
|
||||||
if (_contentReady && _updatedUnreadCount) {
|
|
||||||
[self _internalComplete];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_internalComplete {
|
|
||||||
#if DEBUG
|
|
||||||
reportMemory();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
NSString *baseAppBundleId = _baseAppBundleId;
|
|
||||||
void (^contentHandler)(UNNotificationContent *) = [_contentHandler copy];
|
|
||||||
UNMutableNotificationContent *bestAttemptContent = _bestAttemptContent;
|
|
||||||
NSNumber *updatedUnreadCount = updatedUnreadCount;
|
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
#ifdef __IPHONE_13_0
|
|
||||||
if (baseAppBundleId != nil && false) {
|
|
||||||
BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:[baseAppBundleId stringByAppendingString:@".refresh"]];
|
|
||||||
request.earliestBeginDate = nil;
|
|
||||||
NSError *error = nil;
|
|
||||||
[[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error];
|
|
||||||
if (error != nil) {
|
|
||||||
NSLog(@"Error: %@", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (updatedUnreadCount != nil) {
|
|
||||||
int32_t unreadCount = (int32_t)[updatedUnreadCount intValue];
|
|
||||||
if (unreadCount > 0) {
|
|
||||||
bestAttemptContent.badge = @(unreadCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
contentHandler(bestAttemptContent);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
|
|
||||||
if (_rootPath == nil) {
|
|
||||||
_bestAttemptContent = (UNMutableNotificationContent *)[request.content mutableCopy];
|
|
||||||
[self completeWithBestAttemptContent];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_contentHandler = [contentHandler copy];
|
|
||||||
_bestAttemptContent = (UNMutableNotificationContent *)[request.content mutableCopy];
|
|
||||||
|
|
||||||
NSString * _Nullable encryptedPayload = request.content.userInfo[@"p"];
|
|
||||||
NSData * _Nullable encryptedData = nil;
|
|
||||||
if (encryptedPayload != nil && [encryptedPayload isKindOfClass:[NSString class]]) {
|
|
||||||
encryptedData = parseBase64(encryptedPayload);
|
|
||||||
}
|
|
||||||
|
|
||||||
StoredAccountInfos * _Nullable accountInfos = [StoredAccountInfos loadFromPath:[_rootPath stringByAppendingPathComponent:@"accounts-shared-data"]];
|
|
||||||
|
|
||||||
int selectedAccountIndex = -1;
|
|
||||||
NSDictionary *decryptedPayload = decryptedNotificationPayload(accountInfos.accounts, encryptedData, &selectedAccountIndex);
|
|
||||||
|
|
||||||
if (decryptedPayload != nil && selectedAccountIndex != -1) {
|
|
||||||
StoredAccountInfo *account = accountInfos.accounts[selectedAccountIndex];
|
|
||||||
|
|
||||||
NSMutableDictionary *userInfo = nil;
|
|
||||||
if (_bestAttemptContent.userInfo != nil) {
|
|
||||||
userInfo = [[NSMutableDictionary alloc] initWithDictionary:_bestAttemptContent.userInfo];
|
|
||||||
} else {
|
|
||||||
userInfo = [[NSMutableDictionary alloc] init];
|
|
||||||
}
|
|
||||||
userInfo[@"accountId"] = @(account.accountId);
|
|
||||||
|
|
||||||
int64_t peerId = 0;
|
|
||||||
int32_t messageId = 0;
|
|
||||||
bool silent = false;
|
|
||||||
|
|
||||||
NSString *messageIdString = decryptedPayload[@"msg_id"];
|
|
||||||
if ([messageIdString isKindOfClass:[NSString class]]) {
|
|
||||||
userInfo[@"msg_id"] = messageIdString;
|
|
||||||
messageId = [messageIdString intValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *fromIdString = decryptedPayload[@"from_id"];
|
|
||||||
if ([fromIdString isKindOfClass:[NSString class]]) {
|
|
||||||
userInfo[@"from_id"] = fromIdString;
|
|
||||||
peerId = makePeerId(PeerNamespaceCloudUser, [fromIdString intValue]);
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *chatIdString = decryptedPayload[@"chat_id"];
|
|
||||||
if ([chatIdString isKindOfClass:[NSString class]]) {
|
|
||||||
userInfo[@"chat_id"] = chatIdString;
|
|
||||||
peerId = makePeerId(PeerNamespaceCloudGroup, [chatIdString intValue]);
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *channelIdString = decryptedPayload[@"channel_id"];
|
|
||||||
if ([channelIdString isKindOfClass:[NSString class]]) {
|
|
||||||
userInfo[@"channel_id"] = channelIdString;
|
|
||||||
peerId = makePeerId(PeerNamespaceCloudChannel, [channelIdString intValue]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*if (_countIncomingMessage && _deviceSpecificEncryptionParameters) {
|
|
||||||
_countIncomingMessage(_rootPath, account.accountId, _deviceSpecificEncryptionParameters, peerId, messageId);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
NSString *silentString = decryptedPayload[@"silent"];
|
|
||||||
if ([silentString isKindOfClass:[NSString class]]) {
|
|
||||||
silent = [silentString intValue] != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSData *attachmentData = nil;
|
|
||||||
id parsedAttachment = nil;
|
|
||||||
|
|
||||||
if (!_isLockedValue) {
|
|
||||||
NSString *attachmentDataString = decryptedPayload[@"attachb64"];
|
|
||||||
if ([attachmentDataString isKindOfClass:[NSString class]]) {
|
|
||||||
attachmentData = parseBase64(attachmentDataString);
|
|
||||||
if (attachmentData != nil) {
|
|
||||||
parsedAttachment = parseAttachment(attachmentData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *imagesPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"aps-data"];
|
|
||||||
[[NSFileManager defaultManager] createDirectoryAtPath:imagesPath withIntermediateDirectories:true attributes:nil error:nil];
|
|
||||||
NSString *accountBasePath = [_rootPath stringByAppendingPathComponent:[NSString stringWithFormat:@"account-%llud", account.accountId]];
|
|
||||||
|
|
||||||
NSString *mediaBoxPath = [accountBasePath stringByAppendingPathComponent:@"/postbox/media"];
|
|
||||||
|
|
||||||
NSString *tempImagePath = nil;
|
|
||||||
NSString *mediaBoxThumbnailImagePath = nil;
|
|
||||||
|
|
||||||
int32_t fileDatacenterId = 0;
|
|
||||||
Api1_InputFileLocation *inputFileLocation = nil;
|
|
||||||
int32_t progressiveFileLimit = -1;
|
|
||||||
|
|
||||||
NSString *fetchResourceId = nil;
|
|
||||||
bool isPng = false;
|
|
||||||
bool isExpandableMedia = false;
|
|
||||||
|
|
||||||
if (parsedAttachment != nil) {
|
|
||||||
if ([parsedAttachment isKindOfClass:[Api1_Photo_photo class]]) {
|
|
||||||
Api1_Photo_photo *photo = parsedAttachment;
|
|
||||||
isExpandableMedia = true;
|
|
||||||
|
|
||||||
/*for (id size in photo.sizes) {
|
|
||||||
if ([size isKindOfClass:[Api1_PhotoSize_photoSizeProgressive class]]) {
|
|
||||||
Api1_PhotoSize_photoSizeProgressive *sizeValue = size;
|
|
||||||
inputFileLocation = [Api1_InputFileLocation inputPhotoFileLocationWithPid:photo.pid accessHash:photo.accessHash fileReference:photo.fileReference thumbSize:sizeValue.type];
|
|
||||||
fileDatacenterId = [photo.dcId intValue];
|
|
||||||
fetchResourceId = [NSString stringWithFormat:@"telegram-cloud-photo-size-%@-%@-%@", photo.dcId, photo.pid, sizeValue.type];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if (inputFileLocation == nil) {
|
|
||||||
for (id size in photo.sizes) {
|
|
||||||
if ([size isKindOfClass:[Api1_PhotoSize_photoSize class]]) {
|
|
||||||
Api1_PhotoSize_photoSize *sizeValue = size;
|
|
||||||
if ([sizeValue.type isEqualToString:@"m"]) {
|
|
||||||
inputFileLocation = [Api1_InputFileLocation inputPhotoFileLocationWithPid:photo.pid accessHash:photo.accessHash fileReference:photo.fileReference thumbSize:sizeValue.type];
|
|
||||||
fileDatacenterId = [photo.dcId intValue];
|
|
||||||
fetchResourceId = [NSString stringWithFormat:@"telegram-cloud-photo-size-%@-%@-%@", photo.dcId, photo.pid, sizeValue.type];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ([parsedAttachment isKindOfClass:[Api1_Document_document class]]) {
|
|
||||||
Api1_Document_document *document = parsedAttachment;
|
|
||||||
|
|
||||||
bool isSticker = false;
|
|
||||||
for (id attribute in document.attributes) {
|
|
||||||
if ([attribute isKindOfClass:[Api1_DocumentAttribute_documentAttributeSticker class]]) {
|
|
||||||
isSticker = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool isAnimatedSticker = [document.mimeType isEqualToString:@"application/x-tgsticker"];
|
|
||||||
if (isSticker || isAnimatedSticker) {
|
|
||||||
isExpandableMedia = true;
|
|
||||||
}
|
|
||||||
for (id size in document.thumbs) {
|
|
||||||
if ([size isKindOfClass:[Api1_PhotoSize_photoSize class]]) {
|
|
||||||
Api1_PhotoSize_photoSize *photoSize = size;
|
|
||||||
if ((isSticker && [photoSize.type isEqualToString:@"s"]) || [photoSize.type isEqualToString:@"m"]) {
|
|
||||||
if (isSticker) {
|
|
||||||
isPng = true;
|
|
||||||
}
|
|
||||||
inputFileLocation = [Api1_InputFileLocation inputDocumentFileLocationWithPid:document.pid accessHash:document.accessHash fileReference:document.fileReference thumbSize:photoSize.type];
|
|
||||||
fileDatacenterId = [document.dcId intValue];
|
|
||||||
fetchResourceId = [NSString stringWithFormat:@"telegram-cloud-document-size-%@-%@-%@", document.dcId, document.pid, photoSize.type];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fetchResourceId != nil) {
|
|
||||||
tempImagePath = [imagesPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", fetchResourceId, isPng ? @"png" : @"jpg"]];
|
|
||||||
mediaBoxThumbnailImagePath = [mediaBoxPath stringByAppendingPathComponent:fetchResourceId];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDictionary *aps = decryptedPayload[@"aps"];
|
|
||||||
if ([aps isKindOfClass:[NSDictionary class]]) {
|
|
||||||
id alert = aps[@"alert"];
|
|
||||||
if ([alert isKindOfClass:[NSDictionary class]]) {
|
|
||||||
NSDictionary *alertDict = alert;
|
|
||||||
NSString *title = alertDict[@"title"];
|
|
||||||
NSString *subtitle = alertDict[@"subtitle"];
|
|
||||||
NSString *body = alertDict[@"body"];
|
|
||||||
if (![title isKindOfClass:[NSString class]]) {
|
|
||||||
title = @"";
|
|
||||||
}
|
|
||||||
if (![subtitle isKindOfClass:[NSString class]]) {
|
|
||||||
subtitle = @"";
|
|
||||||
}
|
|
||||||
if (![body isKindOfClass:[NSString class]]) {
|
|
||||||
body = nil;
|
|
||||||
}
|
|
||||||
if (title.length != 0 && silent) {
|
|
||||||
title = [title stringByAppendingString:@" 🔕"];
|
|
||||||
}
|
|
||||||
_bestAttemptContent.title = title;
|
|
||||||
if (_isLockedValue) {
|
|
||||||
_bestAttemptContent.title = @"";
|
|
||||||
_bestAttemptContent.subtitle = @"";
|
|
||||||
if (_lockedMessageTextValue != nil) {
|
|
||||||
_bestAttemptContent.body = _lockedMessageTextValue;
|
|
||||||
} else {
|
|
||||||
_bestAttemptContent.body = @"^You have a new message";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_bestAttemptContent.subtitle = subtitle;
|
|
||||||
_bestAttemptContent.body = body;
|
|
||||||
}
|
|
||||||
} else if ([alert isKindOfClass:[NSString class]]) {
|
|
||||||
_bestAttemptContent.title = @"";
|
|
||||||
_bestAttemptContent.subtitle = @"";
|
|
||||||
if (_isLockedValue) {
|
|
||||||
if (_lockedMessageTextValue != nil) {
|
|
||||||
_bestAttemptContent.body = _lockedMessageTextValue;
|
|
||||||
} else {
|
|
||||||
_bestAttemptContent.body = @"^You have a new message";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_bestAttemptContent.body = alert;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_isLockedValue) {
|
|
||||||
_bestAttemptContent.threadIdentifier = @"locked";
|
|
||||||
} else {
|
|
||||||
NSString *threadIdString = aps[@"thread-id"];
|
|
||||||
if ([threadIdString isKindOfClass:[NSString class]]) {
|
|
||||||
_bestAttemptContent.threadIdentifier = threadIdString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NSString *soundString = aps[@"sound"];
|
|
||||||
if ([soundString isKindOfClass:[NSString class]]) {
|
|
||||||
_bestAttemptContent.sound = [UNNotificationSound soundNamed:soundString];
|
|
||||||
}
|
|
||||||
if (_isLockedValue) {
|
|
||||||
_bestAttemptContent.categoryIdentifier = @"locked";
|
|
||||||
} else {
|
|
||||||
NSString *categoryString = aps[@"category"];
|
|
||||||
if ([categoryString isKindOfClass:[NSString class]]) {
|
|
||||||
_bestAttemptContent.categoryIdentifier = categoryString;
|
|
||||||
if (peerId != 0 && messageId != 0 && parsedAttachment != nil && attachmentData != nil) {
|
|
||||||
userInfo[@"peerId"] = @(peerId);
|
|
||||||
userInfo[@"messageId.namespace"] = @(0);
|
|
||||||
userInfo[@"messageId.id"] = @(messageId);
|
|
||||||
|
|
||||||
userInfo[@"media"] = [attachmentData base64EncodedStringWithOptions:0];
|
|
||||||
|
|
||||||
if (isExpandableMedia) {
|
|
||||||
if ([categoryString isEqualToString:@"r"]) {
|
|
||||||
_bestAttemptContent.categoryIdentifier = @"withReplyMedia";
|
|
||||||
} else if ([categoryString isEqualToString:@"m"]) {
|
|
||||||
_bestAttemptContent.categoryIdentifier = @"withMuteMedia";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accountInfos.accounts.count > 1) {
|
|
||||||
if (_bestAttemptContent.title.length != 0 && account.peerName.length != 0) {
|
|
||||||
_bestAttemptContent.title = [NSString stringWithFormat:@"%@ → %@", _bestAttemptContent.title, account.peerName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_bestAttemptContent.userInfo = userInfo;
|
|
||||||
|
|
||||||
if (_cancelFetch) {
|
|
||||||
_cancelFetch();
|
|
||||||
_cancelFetch = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mediaBoxThumbnailImagePath != nil && tempImagePath != nil && inputFileLocation != nil) {
|
|
||||||
NSData *data = [NSData dataWithContentsOfFile:mediaBoxThumbnailImagePath];
|
|
||||||
if (data != nil) {
|
|
||||||
NSData *tempData = data;
|
|
||||||
if (isPng) {
|
|
||||||
/*if let image = WebP.convert(fromWebP: data), let imageData = image.pngData() {
|
|
||||||
tempData = imageData
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
if ([tempData writeToFile:tempImagePath atomically:true]) {
|
|
||||||
UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:@"image" URL:[NSURL fileURLWithPath:tempImagePath] options:nil error:nil];
|
|
||||||
if (attachment != nil) {
|
|
||||||
_bestAttemptContent.attachments = @[attachment];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[self completeWithBestAttemptContent];
|
|
||||||
} else {
|
|
||||||
BuildConfig *buildConfig = [[BuildConfig alloc] initWithBaseAppBundleId:_baseAppBundleId];
|
|
||||||
|
|
||||||
void (^serialDispatch)(dispatch_block_t) = _serialDispatch;
|
|
||||||
|
|
||||||
__weak typeof(self) weakSelf = self;
|
|
||||||
_cancelFetch = fetchImage(buildConfig, accountInfos.proxy, account, inputFileLocation, fileDatacenterId, ^(NSData * _Nullable data) {
|
|
||||||
serialDispatch(^{
|
|
||||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
|
||||||
if (strongSelf == nil) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (strongSelf->_cancelFetch) {
|
|
||||||
strongSelf->_cancelFetch();
|
|
||||||
strongSelf->_cancelFetch = nil;
|
|
||||||
|
|
||||||
if (data != nil) {
|
|
||||||
[data writeToFile:mediaBoxThumbnailImagePath atomically:true];
|
|
||||||
NSData *tempData = data;
|
|
||||||
if (isPng) {
|
|
||||||
/*if let image = WebP.convert(fromWebP: data), let imageData = image.pngData() {
|
|
||||||
tempData = imageData
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
if ([tempData writeToFile:tempImagePath atomically:true]) {
|
|
||||||
UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:@"image" URL:[NSURL fileURLWithPath:tempImagePath] options:nil error:nil];
|
|
||||||
if (attachment != nil) {
|
|
||||||
strongSelf->_bestAttemptContent.attachments = @[attachment];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[strongSelf completeWithBestAttemptContent];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
[self completeWithBestAttemptContent];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
[self completeWithBestAttemptContent];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)serviceExtensionTimeWillExpire {
|
|
||||||
if (_cancelFetch) {
|
|
||||||
_cancelFetch();
|
|
||||||
_cancelFetch = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_contentHandler) {
|
|
||||||
if(_bestAttemptContent) {
|
|
||||||
if (_updatedUnreadCount == nil) {
|
|
||||||
_updatedUnreadCount = @(-1);
|
|
||||||
}
|
|
||||||
[self completeWithBestAttemptContent];
|
|
||||||
}
|
|
||||||
_contentHandler = nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -1,11 +0,0 @@
|
|||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
#import <MTProtoKit/MTProtoKit.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface Serialization : NSObject <MTSerialization>
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -1,35 +0,0 @@
|
|||||||
#import "Serialization.h"
|
|
||||||
|
|
||||||
@implementation Serialization
|
|
||||||
|
|
||||||
- (NSUInteger)currentLayer {
|
|
||||||
return 133;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id _Nullable)parseMessage:(NSData * _Nullable)data {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MTExportAuthorizationResponseParser _Nonnull)exportAuthorization:(int32_t)datacenterId data:(__autoreleasing NSData **)data {
|
|
||||||
return ^MTExportedAuthorizationData *(NSData *resultData) {
|
|
||||||
return nil;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSData * _Nonnull)importAuthorization:(int64_t)authId bytes:(NSData * _Nonnull)bytes {
|
|
||||||
return [NSData data];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MTRequestDatacenterAddressListParser)requestDatacenterAddressWithData:(__autoreleasing NSData **)data {
|
|
||||||
return ^MTDatacenterAddressListData *(NSData *resultData) {
|
|
||||||
return nil;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MTRequestNoopParser)requestNoop:(__autoreleasing NSData **)data {
|
|
||||||
return ^id(NSData *resultData) {
|
|
||||||
return nil;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -1,69 +0,0 @@
|
|||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface AccountNotificationKey: NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSData *keyId;
|
|
||||||
@property (nonatomic, strong, readonly) NSData *data;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface AccountDatacenterKey: NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, readonly) int64_t keyId;
|
|
||||||
@property (nonatomic, strong, readonly) NSData *data;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface AccountDatacenterAddress: NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSString *host;
|
|
||||||
@property (nonatomic, readonly) int32_t port;
|
|
||||||
@property (nonatomic, readonly) bool isMedia;
|
|
||||||
@property (nonatomic, strong, readonly) NSData * _Nullable secret;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface AccountDatacenterInfo: NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) AccountDatacenterKey *masterKey;
|
|
||||||
@property (nonatomic, strong, readonly) AccountDatacenterKey *ephemeralMainKey;
|
|
||||||
@property (nonatomic, strong, readonly) AccountDatacenterKey *ephemeralMediaKey;
|
|
||||||
@property (nonatomic, strong, readonly) NSArray<AccountDatacenterAddress *> *addressList;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface AccountProxyConnection: NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSString *host;
|
|
||||||
@property (nonatomic, readonly) int32_t port;
|
|
||||||
@property (nonatomic, strong) NSString * _Nullable username;
|
|
||||||
@property (nonatomic, strong) NSString * _Nullable password;
|
|
||||||
@property (nonatomic, strong) NSData * _Nullable secret;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface StoredAccountInfo : NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, readonly) int64_t accountId;
|
|
||||||
@property (nonatomic, readonly) int32_t primaryId;
|
|
||||||
@property (nonatomic, readonly) bool isTestingEnvironment;
|
|
||||||
@property (nonatomic, strong, readonly) NSString *peerName;
|
|
||||||
@property (nonatomic, strong, readonly) NSDictionary<NSNumber *, AccountDatacenterInfo *> *datacenters;
|
|
||||||
@property (nonatomic, strong, readonly) AccountNotificationKey *notificationKey;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface StoredAccountInfos : NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) AccountProxyConnection * _Nullable proxy;
|
|
||||||
@property (nonatomic, strong, readonly) NSArray<StoredAccountInfo *> *accounts;
|
|
||||||
|
|
||||||
+ (StoredAccountInfos * _Nullable)loadFromPath:(NSString *)path;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NSDictionary * _Nullable decryptedNotificationPayload(NSArray<StoredAccountInfo *> *accounts, NSData *data, int *selectedAccountIndex);
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -1,427 +0,0 @@
|
|||||||
#import "StoredAccountInfos.h"
|
|
||||||
|
|
||||||
#import <MTProtoKit/MTProtoKit.h>
|
|
||||||
|
|
||||||
#import <CommonCrypto/CommonDigest.h>
|
|
||||||
|
|
||||||
@implementation AccountNotificationKey
|
|
||||||
|
|
||||||
- (instancetype)initWithKeyId:(NSData *)keyId data:(NSData *)data {
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil) {
|
|
||||||
_keyId = keyId;
|
|
||||||
_data = data;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (instancetype _Nullable)parse:(NSDictionary *)dict {
|
|
||||||
NSString *keyIdString = dict[@"id"];
|
|
||||||
NSData *keyId = nil;
|
|
||||||
if ([keyIdString isKindOfClass:[NSString class]]) {
|
|
||||||
keyId = [[NSData alloc] initWithBase64EncodedString:keyIdString options:0];
|
|
||||||
}
|
|
||||||
if (keyId == nil) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *dataString = dict[@"data"];
|
|
||||||
NSData *data = nil;
|
|
||||||
if ([dataString isKindOfClass:[NSString class]]) {
|
|
||||||
data = [[NSData alloc] initWithBase64EncodedString:dataString options:0];
|
|
||||||
}
|
|
||||||
if (data == nil) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [[AccountNotificationKey alloc] initWithKeyId:keyId data:data];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation AccountDatacenterKey
|
|
||||||
|
|
||||||
- (instancetype)initWithKeyId:(int64_t)keyId data:(NSData *)data {
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil) {
|
|
||||||
_keyId = keyId;
|
|
||||||
_data = data;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (instancetype _Nullable)parse:(NSDictionary *)dict {
|
|
||||||
NSNumber *keyIdNumber = dict[@"id"];
|
|
||||||
if (![keyIdNumber isKindOfClass:[NSNumber class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
int64_t keyId = [keyIdNumber longLongValue];
|
|
||||||
|
|
||||||
NSString *dataString = dict[@"data"];
|
|
||||||
NSData *data = nil;
|
|
||||||
if ([dataString isKindOfClass:[NSString class]]) {
|
|
||||||
data = [[NSData alloc] initWithBase64EncodedString:dataString options:0];
|
|
||||||
}
|
|
||||||
if (data == nil) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [[AccountDatacenterKey alloc] initWithKeyId:keyId data:data];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation AccountDatacenterAddress
|
|
||||||
|
|
||||||
- (instancetype)initWithHost:(NSString *)host port:(int32_t)port isMedia:(bool)isMedia secret:(NSData * _Nullable)secret {
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil) {
|
|
||||||
_host = host;
|
|
||||||
_port = port;
|
|
||||||
_isMedia = isMedia;
|
|
||||||
_secret = secret;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (instancetype _Nullable)parse:(NSDictionary *)dict {
|
|
||||||
NSString *hostString = dict[@"host"];
|
|
||||||
if (![hostString isKindOfClass:[NSString class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
NSString *host = hostString;
|
|
||||||
|
|
||||||
NSNumber *portNumber = dict[@"port"];
|
|
||||||
if (![portNumber isKindOfClass:[NSNumber class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
int32_t port = [portNumber intValue];
|
|
||||||
|
|
||||||
NSNumber *isMediaNumber = dict[@"isMedia"];
|
|
||||||
if (![isMediaNumber isKindOfClass:[NSNumber class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
bool isMedia = [isMediaNumber intValue] != 0;
|
|
||||||
|
|
||||||
NSString *secretString = dict[@"secret"];
|
|
||||||
NSData *secret = nil;
|
|
||||||
if ([secretString isKindOfClass:[NSString class]]) {
|
|
||||||
secret = [[NSData alloc] initWithBase64EncodedString:secretString options:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [[AccountDatacenterAddress alloc] initWithHost:host port:port isMedia:isMedia secret:secret];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation AccountDatacenterInfo
|
|
||||||
|
|
||||||
- (instancetype)initWithMasterKey:(AccountDatacenterKey *)masterKey ephemeralMainKey:(AccountDatacenterKey * _Nullable)ephemeralMainKey ephemeralMediaKey:(AccountDatacenterKey * _Nullable)ephemeralMediaKey addressList:(NSArray<AccountDatacenterAddress *> *)addressList {
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil) {
|
|
||||||
_masterKey = masterKey;
|
|
||||||
_ephemeralMainKey = ephemeralMainKey;
|
|
||||||
_ephemeralMediaKey = ephemeralMediaKey;
|
|
||||||
_addressList = addressList;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (instancetype _Nullable)parse:(NSDictionary *)dict {
|
|
||||||
NSDictionary *masterKeyDict = dict[@"masterKey"];
|
|
||||||
if (![masterKeyDict isKindOfClass:[NSDictionary class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
AccountDatacenterKey *masterKey = [AccountDatacenterKey parse:masterKeyDict];
|
|
||||||
if (masterKey == nil) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDictionary *ephemeralMainKeyDict = dict[@"ephemeralMainKey"];
|
|
||||||
AccountDatacenterKey *ephemeralMainKey = nil;
|
|
||||||
if ([ephemeralMainKeyDict isKindOfClass:[NSDictionary class]]) {
|
|
||||||
ephemeralMainKey = [AccountDatacenterKey parse:ephemeralMainKeyDict];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDictionary *ephemeralMediaKeyDict = dict[@"ephemeralMediaKey"];
|
|
||||||
AccountDatacenterKey *ephemeralMediaKey = nil;
|
|
||||||
if ([ephemeralMediaKeyDict isKindOfClass:[NSDictionary class]]) {
|
|
||||||
ephemeralMediaKey = [AccountDatacenterKey parse:ephemeralMediaKeyDict];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSArray *addressListArray = dict[@"addressList"];
|
|
||||||
if (![addressListArray isKindOfClass:[NSArray class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSMutableArray<AccountDatacenterAddress *> *addressList = [[NSMutableArray alloc] init];
|
|
||||||
for (NSDictionary *addressListItem in addressListArray) {
|
|
||||||
if (![addressListItem isKindOfClass:[NSDictionary class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
AccountDatacenterAddress *address = [AccountDatacenterAddress parse:addressListItem];
|
|
||||||
if (address == nil) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
[addressList addObject:address];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [[AccountDatacenterInfo alloc] initWithMasterKey:masterKey ephemeralMainKey:ephemeralMainKey ephemeralMediaKey:ephemeralMediaKey addressList:addressList];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation AccountProxyConnection
|
|
||||||
|
|
||||||
- (instancetype)initWithHost:(NSString *)host port:(int32_t)port username:(NSString * _Nullable)username password:(NSString * _Nullable)password secret:(NSData * _Nullable)secret {
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil) {
|
|
||||||
_host = host;
|
|
||||||
_port = port;
|
|
||||||
username = _username;
|
|
||||||
password = _password;
|
|
||||||
secret = _secret;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (instancetype _Nullable)parse:(NSDictionary *)dict {
|
|
||||||
NSString *hostString = dict[@"host"];
|
|
||||||
if (![hostString isKindOfClass:[NSString class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
NSString *host = hostString;
|
|
||||||
|
|
||||||
NSNumber *portNumber = dict[@"port"];
|
|
||||||
if (![portNumber isKindOfClass:[NSNumber class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t port = [portNumber intValue];
|
|
||||||
|
|
||||||
NSString *usernameString = dict[@"username"];
|
|
||||||
NSString *username = nil;
|
|
||||||
if ([usernameString isKindOfClass:[NSString class]]) {
|
|
||||||
username = usernameString;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *passwordString = dict[@"password"];
|
|
||||||
NSString *password = nil;
|
|
||||||
if ([passwordString isKindOfClass:[NSString class]]) {
|
|
||||||
password = passwordString;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *secretString = dict[@"secret"];
|
|
||||||
NSData *secret = nil;
|
|
||||||
if ([secretString isKindOfClass:[NSString class]]) {
|
|
||||||
secret = [[NSData alloc] initWithBase64EncodedString:secretString options:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [[AccountProxyConnection alloc] initWithHost:host port:port username:username password:password secret:secret];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation StoredAccountInfo
|
|
||||||
|
|
||||||
- (instancetype)initWithAccountId:(int64_t)accountId primaryId:(int32_t)primaryId isTestingEnvironment:(bool)isTestingEnvironment peerName:(NSString *)peerName datacenters:(NSDictionary<NSNumber *, AccountDatacenterInfo *> *)datacenters notificationKey:(AccountNotificationKey *)notificationKey {
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil) {
|
|
||||||
_accountId = accountId;
|
|
||||||
_primaryId = primaryId;
|
|
||||||
_isTestingEnvironment = isTestingEnvironment;
|
|
||||||
_peerName = peerName;
|
|
||||||
_datacenters = datacenters;
|
|
||||||
_notificationKey = notificationKey;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (instancetype _Nullable)parse:(NSDictionary *)dict {
|
|
||||||
NSNumber *idNumber = dict[@"id"];
|
|
||||||
if (![idNumber isKindOfClass:[NSNumber class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
int64_t accountId = [idNumber longLongValue];
|
|
||||||
|
|
||||||
NSNumber *primaryIdNumber = dict[@"primaryId"];
|
|
||||||
if (![primaryIdNumber isKindOfClass:[NSNumber class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t primaryId = [primaryIdNumber intValue];
|
|
||||||
|
|
||||||
NSNumber *isTestingEnvironmentNumber = dict[@"isTestingEnvironment"];
|
|
||||||
if (![isTestingEnvironmentNumber isKindOfClass:[NSNumber class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isTestingEnvironment = [isTestingEnvironmentNumber intValue] != 0;
|
|
||||||
|
|
||||||
NSString *peerNameString = dict[@"peerName"];
|
|
||||||
if (![peerNameString isKindOfClass:[NSString class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *peerName = peerNameString;
|
|
||||||
|
|
||||||
NSArray *datacentersArray = dict[@"datacenters"];
|
|
||||||
if (![datacentersArray isKindOfClass:[NSArray class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSMutableDictionary<NSNumber *, AccountDatacenterInfo *> *datacenters = [[NSMutableDictionary alloc] init];
|
|
||||||
|
|
||||||
for (NSInteger i = 0; i < datacentersArray.count; i += 2) {
|
|
||||||
NSNumber *datacenterKey = datacentersArray[i];
|
|
||||||
NSDictionary *datacenterData = datacentersArray[i + 1];
|
|
||||||
|
|
||||||
if (![datacenterKey isKindOfClass:[NSNumber class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
if (![datacenterData isKindOfClass:[NSDictionary class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
AccountDatacenterInfo *parsedDatacenter = [AccountDatacenterInfo parse:datacenterData];
|
|
||||||
if (parsedDatacenter != nil) {
|
|
||||||
datacenters[datacenterKey] = parsedDatacenter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDictionary *notificationKeyDict = dict[@"notificationKey"];
|
|
||||||
if (![notificationKeyDict isKindOfClass:[NSDictionary class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
AccountNotificationKey *notificationKey = [AccountNotificationKey parse:notificationKeyDict];
|
|
||||||
if (notificationKey == nil) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [[StoredAccountInfo alloc] initWithAccountId:accountId primaryId:primaryId isTestingEnvironment:isTestingEnvironment peerName:peerName datacenters:datacenters notificationKey:notificationKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation StoredAccountInfos
|
|
||||||
|
|
||||||
- (instancetype)initWithProxy:(AccountProxyConnection * _Nullable)proxy accounts:(NSArray<StoredAccountInfo *> *)accounts {
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil) {
|
|
||||||
_proxy = proxy;
|
|
||||||
_accounts = accounts;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (StoredAccountInfos * _Nullable)loadFromPath:(NSString *)path {
|
|
||||||
NSData *data = [NSData dataWithContentsOfFile:path];
|
|
||||||
if (data == nil) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
|
||||||
if (![dict isKindOfClass:[NSDictionary class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
AccountProxyConnection * _Nullable proxy = nil;
|
|
||||||
NSDictionary *proxyDict = dict[@"proxy"];
|
|
||||||
if ([proxyDict isKindOfClass:[NSDictionary class]]) {
|
|
||||||
proxy = [AccountProxyConnection parse:proxyDict];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSMutableArray<StoredAccountInfo *> *accounts = [[NSMutableArray alloc] init];
|
|
||||||
|
|
||||||
NSArray *accountsObject = dict[@"accounts"];
|
|
||||||
if ([accountsObject isKindOfClass:[NSArray class]]) {
|
|
||||||
for (NSDictionary *object in accountsObject) {
|
|
||||||
if ([object isKindOfClass:[NSDictionary class]]) {
|
|
||||||
StoredAccountInfo *account = [StoredAccountInfo parse:object];
|
|
||||||
if (account != nil) {
|
|
||||||
[accounts addObject:account];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [[StoredAccountInfos alloc] initWithProxy:proxy accounts:accounts];;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
static NSData *sha256Digest(NSData *data) {
|
|
||||||
uint8_t digest[CC_SHA256_DIGEST_LENGTH];
|
|
||||||
CC_SHA256(data.bytes, (CC_LONG)data.length, digest);
|
|
||||||
|
|
||||||
return [[NSData alloc] initWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
|
|
||||||
}
|
|
||||||
|
|
||||||
static NSData *concatData(NSData *data1, NSData *data2) {
|
|
||||||
NSMutableData *data = [[NSMutableData alloc] initWithCapacity:data1.length + data2.length];
|
|
||||||
[data appendData:data1];
|
|
||||||
[data appendData:data2];
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static NSData *concatData3(NSData *data1, NSData *data2, NSData *data3) {
|
|
||||||
NSMutableData *data = [[NSMutableData alloc] initWithCapacity:data1.length + data2.length + data3.length];
|
|
||||||
[data appendData:data1];
|
|
||||||
[data appendData:data2];
|
|
||||||
[data appendData:data3];
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDictionary * _Nullable decryptedNotificationPayload(NSArray<StoredAccountInfo *> *accounts, NSData *data, int *selectedAccountIndex) {
|
|
||||||
if (data.length < 8 + 16) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
int accountIndex = -1;
|
|
||||||
for (StoredAccountInfo *account in accounts) {
|
|
||||||
accountIndex += 1;
|
|
||||||
|
|
||||||
AccountNotificationKey *notificationKey = account.notificationKey;
|
|
||||||
if (![[data subdataWithRange:NSMakeRange(0, 8)] isEqualToData:notificationKey.keyId]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int x = 8;
|
|
||||||
NSData *msgKey = [data subdataWithRange:NSMakeRange(8, 16)];
|
|
||||||
NSData *rawData = [data subdataWithRange:NSMakeRange(8 + 16, data.length - (8 + 16))];
|
|
||||||
|
|
||||||
NSData *sha256_a = sha256Digest(concatData(msgKey, [notificationKey.data subdataWithRange:NSMakeRange(x, 36)]));
|
|
||||||
NSData *sha256_b = sha256Digest(concatData([notificationKey.data subdataWithRange:NSMakeRange(40 + x, 36)], msgKey));
|
|
||||||
NSData *aesKey = concatData3([sha256_a subdataWithRange:NSMakeRange(0, 8)], [sha256_b subdataWithRange:NSMakeRange(8, 16)], [sha256_a subdataWithRange:NSMakeRange(24, 8)]);
|
|
||||||
NSData *aesIv = concatData3([sha256_b subdataWithRange:NSMakeRange(0, 8)], [sha256_a subdataWithRange:NSMakeRange(8, 16)], [sha256_b subdataWithRange:NSMakeRange(24, 8)]);
|
|
||||||
|
|
||||||
NSData *decryptedData = MTAesDecrypt(rawData, aesKey, aesIv);
|
|
||||||
if (decryptedData.length <= 4) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t dataLength = 0;
|
|
||||||
[decryptedData getBytes:&dataLength range:NSMakeRange(0, 4)];
|
|
||||||
|
|
||||||
if (dataLength < 0 || dataLength > decryptedData.length - 4) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSData *checkMsgKeyLarge = sha256Digest(concatData([notificationKey.data subdataWithRange:NSMakeRange(88 + x, 32)], decryptedData));
|
|
||||||
NSData *checkMsgKey = [checkMsgKeyLarge subdataWithRange:NSMakeRange(8, 16)];
|
|
||||||
|
|
||||||
if (![checkMsgKey isEqualToData:msgKey]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSData *contentData = [decryptedData subdataWithRange:NSMakeRange(4, dataLength)];
|
|
||||||
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:contentData options:0 error:nil];
|
|
||||||
if (![dict isKindOfClass:[NSDictionary class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
if (selectedAccountIndex != nil) {
|
|
||||||
*selectedAccountIndex = accountIndex;
|
|
||||||
}
|
|
||||||
return dict;
|
|
||||||
}
|
|
||||||
return nil;
|
|
||||||
}
|
|
@ -1673,7 +1673,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
}),
|
}),
|
||||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ForcedPasswordSetup_Intro_DismissActionOK, action: { [weak controller] in
|
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ForcedPasswordSetup_Intro_DismissActionOK, action: { [weak controller] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let _ = ApplicationSpecificNotice.setForcedPasswordSetup(postbox: strongSelf.context.account.postbox, reloginDaysTimeout: nil).start()
|
let _ = ApplicationSpecificNotice.setForcedPasswordSetup(engine: strongSelf.context.engine, reloginDaysTimeout: nil).start()
|
||||||
}
|
}
|
||||||
controller?.dismiss()
|
controller?.dismiss()
|
||||||
})
|
})
|
||||||
@ -2560,23 +2560,20 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
}
|
}
|
||||||
|
|
||||||
func toggleArchivedFolderHiddenByDefault() {
|
func toggleArchivedFolderHiddenByDefault() {
|
||||||
let _ = (self.context.account.postbox.transaction { transaction -> Bool in
|
var updatedValue = false
|
||||||
var updatedValue = false
|
let _ = (updateChatArchiveSettings(engine: self.context.engine, { settings in
|
||||||
updateChatArchiveSettings(transaction: transaction, { settings in
|
var settings = settings
|
||||||
var settings = settings
|
settings.isHiddenByDefault = !settings.isHiddenByDefault
|
||||||
settings.isHiddenByDefault = !settings.isHiddenByDefault
|
updatedValue = settings.isHiddenByDefault
|
||||||
updatedValue = settings.isHiddenByDefault
|
return settings
|
||||||
return settings
|
})
|
||||||
})
|
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||||
return updatedValue
|
|
||||||
}
|
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.chatListDisplayNode.containerNode.updateState { state in
|
strongSelf.chatListDisplayNode.containerNode.updateState { state in
|
||||||
var state = state
|
var state = state
|
||||||
if value {
|
if updatedValue {
|
||||||
state.archiveShouldBeTemporaryRevealed = false
|
state.archiveShouldBeTemporaryRevealed = false
|
||||||
}
|
}
|
||||||
state.peerIdWithRevealedOptions = nil
|
state.peerIdWithRevealedOptions = nil
|
||||||
@ -2589,22 +2586,18 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
if value {
|
if updatedValue {
|
||||||
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .hidArchive(title: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenTitle, text: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenText, undo: false), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] value in
|
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .hidArchive(title: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenTitle, text: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenText, undo: false), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] value in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if value == .undo {
|
if value == .undo {
|
||||||
let _ = (strongSelf.context.account.postbox.transaction { transaction -> Bool in
|
let _ = updateChatArchiveSettings(engine: strongSelf.context.engine, { settings in
|
||||||
var updatedValue = false
|
var settings = settings
|
||||||
updateChatArchiveSettings(transaction: transaction, { settings in
|
settings.isHiddenByDefault = false
|
||||||
var settings = settings
|
return settings
|
||||||
settings.isHiddenByDefault = false
|
|
||||||
updatedValue = settings.isHiddenByDefault
|
|
||||||
return settings
|
|
||||||
})
|
|
||||||
return updatedValue
|
|
||||||
}).start()
|
}).start()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
|
||||||
|
|
||||||
swift_library(
|
|
||||||
name = "LightweightAccountData",
|
|
||||||
module_name = "LightweightAccountData",
|
|
||||||
srcs = glob([
|
|
||||||
"Sources/**/*.swift",
|
|
||||||
]),
|
|
||||||
copts = [
|
|
||||||
"-warnings-as-errors",
|
|
||||||
],
|
|
||||||
visibility = [
|
|
||||||
"//visibility:public",
|
|
||||||
],
|
|
||||||
)
|
|
@ -1,103 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
public struct AccountNotificationKey: Codable {
|
|
||||||
public let id: Data
|
|
||||||
public let data: Data
|
|
||||||
|
|
||||||
public init(id: Data, data: Data) {
|
|
||||||
self.id = id
|
|
||||||
self.data = data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AccountDatacenterKey: Codable {
|
|
||||||
public let id: Int64
|
|
||||||
public let data: Data
|
|
||||||
|
|
||||||
public init(id: Int64, data: Data) {
|
|
||||||
self.id = id
|
|
||||||
self.data = data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AccountDatacenterAddress: Codable {
|
|
||||||
public let host: String
|
|
||||||
public let port: Int32
|
|
||||||
public let isMedia: Bool
|
|
||||||
public let secret: Data?
|
|
||||||
|
|
||||||
public init(host: String, port: Int32, isMedia: Bool, secret: Data?) {
|
|
||||||
self.host = host
|
|
||||||
self.port = port
|
|
||||||
self.isMedia = isMedia
|
|
||||||
self.secret = secret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AccountDatacenterInfo: Codable {
|
|
||||||
public let masterKey: AccountDatacenterKey
|
|
||||||
public let ephemeralMainKey: AccountDatacenterKey?
|
|
||||||
public let ephemeralMediaKey: AccountDatacenterKey?
|
|
||||||
public let addressList: [AccountDatacenterAddress]
|
|
||||||
|
|
||||||
public init(masterKey: AccountDatacenterKey, ephemeralMainKey: AccountDatacenterKey?, ephemeralMediaKey: AccountDatacenterKey?, addressList: [AccountDatacenterAddress]) {
|
|
||||||
self.masterKey = masterKey
|
|
||||||
self.ephemeralMainKey = ephemeralMainKey
|
|
||||||
self.ephemeralMediaKey = ephemeralMediaKey
|
|
||||||
self.addressList = addressList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AccountProxyConnection: Codable {
|
|
||||||
public let host: String
|
|
||||||
public let port: Int32
|
|
||||||
public let username: String?
|
|
||||||
public let password: String?
|
|
||||||
public let secret: Data?
|
|
||||||
|
|
||||||
public init(host: String, port: Int32, username: String?, password: String?, secret: Data?) {
|
|
||||||
self.host = host
|
|
||||||
self.port = port
|
|
||||||
self.username = username
|
|
||||||
self.password = password
|
|
||||||
self.secret = secret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct StoredAccountInfo: Codable {
|
|
||||||
public let id: Int64
|
|
||||||
public let primaryId: Int32
|
|
||||||
public let isTestingEnvironment: Bool
|
|
||||||
public let peerName: String
|
|
||||||
public let datacenters: [Int32: AccountDatacenterInfo]
|
|
||||||
public let notificationKey: AccountNotificationKey
|
|
||||||
|
|
||||||
public init(id: Int64, primaryId: Int32, isTestingEnvironment: Bool, peerName: String, datacenters: [Int32: AccountDatacenterInfo], notificationKey: AccountNotificationKey) {
|
|
||||||
self.id = id
|
|
||||||
self.primaryId = primaryId
|
|
||||||
self.isTestingEnvironment = isTestingEnvironment
|
|
||||||
self.peerName = peerName
|
|
||||||
self.datacenters = datacenters
|
|
||||||
self.notificationKey = notificationKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct StoredAccountInfos: Codable {
|
|
||||||
public let proxy: AccountProxyConnection?
|
|
||||||
public let accounts: [StoredAccountInfo]
|
|
||||||
|
|
||||||
public init(proxy: AccountProxyConnection?, accounts: [StoredAccountInfo]) {
|
|
||||||
self.proxy = proxy
|
|
||||||
self.accounts = accounts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func loadAccountsData(rootPath: String) -> StoredAccountInfos {
|
|
||||||
guard let data = try? Data(contentsOf: URL(fileURLWithPath: rootPath + "/accounts-shared-data")) else {
|
|
||||||
return StoredAccountInfos(proxy: nil, accounts: [])
|
|
||||||
}
|
|
||||||
guard let value = try? JSONDecoder().decode(StoredAccountInfos.self, from: data) else {
|
|
||||||
return StoredAccountInfos(proxy: nil, accounts: [])
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
@ -416,13 +416,7 @@ public func dataPrivacyController(context: AccountContext) -> ViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = context.account.postbox.transaction({ transaction in
|
let _ = context.engine.contacts.updateIsContactSynchronizationEnabled(isContactSynchronizationEnabled: false).start()
|
||||||
transaction.updatePreferencesEntry(key: PreferencesKeys.contactsSettings, { current in
|
|
||||||
var settings = current?.get(ContactsSettings.self) ?? ContactsSettings.defaultSettings
|
|
||||||
settings.synchronizeContacts = false
|
|
||||||
return PreferencesEntry(settings)
|
|
||||||
})
|
|
||||||
}).start()
|
|
||||||
|
|
||||||
actionsDisposable.add((context.engine.contacts.deleteAllContacts()
|
actionsDisposable.add((context.engine.contacts.deleteAllContacts()
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
@ -437,13 +431,7 @@ public func dataPrivacyController(context: AccountContext) -> ViewController {
|
|||||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})]))
|
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})]))
|
||||||
}
|
}
|
||||||
}, updateSyncContacts: { value in
|
}, updateSyncContacts: { value in
|
||||||
let _ = context.account.postbox.transaction({ transaction in
|
let _ = context.engine.contacts.updateIsContactSynchronizationEnabled(isContactSynchronizationEnabled: value).start()
|
||||||
transaction.updatePreferencesEntry(key: PreferencesKeys.contactsSettings, { current in
|
|
||||||
var settings = current?.get(ContactsSettings.self) ?? ContactsSettings.defaultSettings
|
|
||||||
settings.synchronizeContacts = value
|
|
||||||
return PreferencesEntry(settings)
|
|
||||||
})
|
|
||||||
}).start()
|
|
||||||
}, updateSuggestFrequentContacts: { value in
|
}, updateSuggestFrequentContacts: { value in
|
||||||
let apply: () -> Void = {
|
let apply: () -> Void = {
|
||||||
updateState { state in
|
updateState { state in
|
||||||
|
@ -58,5 +58,16 @@ public extension TelegramEngine {
|
|||||||
return (peers.map(EnginePeer.init), presences.mapValues(EnginePeer.Presence.init))
|
return (peers.map(EnginePeer.init), presences.mapValues(EnginePeer.Presence.init))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func updateIsContactSynchronizationEnabled(isContactSynchronizationEnabled: Bool) -> Signal<Never, NoError> {
|
||||||
|
return self.account.postbox.transaction { transaction -> Void in
|
||||||
|
transaction.updatePreferencesEntry(key: PreferencesKeys.contactsSettings, { current in
|
||||||
|
var settings = current?.get(ContactsSettings.self) ?? ContactsSettings.defaultSettings
|
||||||
|
settings.synchronizeContacts = isContactSynchronizationEnabled
|
||||||
|
return PreferencesEntry(settings)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|> ignoreValues
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,8 +62,11 @@ public extension TelegramEngine {
|
|||||||
|> ignoreValues
|
|> ignoreValues
|
||||||
}
|
}
|
||||||
|
|
||||||
public func deleteAllMessagesWithForwardAuthor(transaction: Transaction, peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace) {
|
public func deleteAllMessagesWithForwardAuthor(peerId: EnginePeer.Id, forwardAuthorId: EnginePeer.Id, namespace: MessageId.Namespace) -> Signal<Never, NoError> {
|
||||||
return _internal_deleteAllMessagesWithForwardAuthor(transaction: transaction, mediaBox: self.account.postbox.mediaBox, peerId: peerId, forwardAuthorId: forwardAuthorId, namespace: namespace)
|
return self.account.postbox.transaction { transaction -> Void in
|
||||||
|
_internal_deleteAllMessagesWithForwardAuthor(transaction: transaction, mediaBox: self.account.postbox.mediaBox, peerId: peerId, forwardAuthorId: forwardAuthorId, namespace: namespace)
|
||||||
|
}
|
||||||
|
|> ignoreValues
|
||||||
}
|
}
|
||||||
|
|
||||||
public func clearCallHistory(forEveryone: Bool) -> Signal<Never, ClearCallHistoryError> {
|
public func clearCallHistory(forEveryone: Bool) -> Signal<Never, ClearCallHistoryError> {
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
|
||||||
|
public extension TelegramEngine {
|
||||||
|
final class Notices {
|
||||||
|
private let account: Account
|
||||||
|
|
||||||
|
init(account: Account) {
|
||||||
|
self.account = account
|
||||||
|
}
|
||||||
|
|
||||||
|
public func set<T: Codable>(id: NoticeEntryKey, item: T?) -> Signal<Never, NoError> {
|
||||||
|
return self.account.postbox.transaction { transaction -> Void in
|
||||||
|
if let item = item, let entry = CodableEntry(item) {
|
||||||
|
transaction.setNoticeEntry(key: id, value: entry)
|
||||||
|
} else {
|
||||||
|
transaction.setNoticeEntry(key: id, value: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> ignoreValues
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
|
||||||
|
public extension TelegramEngine {
|
||||||
|
final class Preferences {
|
||||||
|
private let account: Account
|
||||||
|
|
||||||
|
init(account: Account) {
|
||||||
|
self.account = account
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(id: ValueBoxKey, _ f: @escaping (PreferencesEntry?) -> PreferencesEntry?) -> Signal<Never, NoError> {
|
||||||
|
return self.account.postbox.transaction { transaction -> Void in
|
||||||
|
transaction.updatePreferencesEntry(key: id, { entry in
|
||||||
|
return f(entry)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|> ignoreValues
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -83,6 +83,14 @@ public final class TelegramEngine {
|
|||||||
public lazy var itemCache: ItemCache = {
|
public lazy var itemCache: ItemCache = {
|
||||||
return ItemCache(account: self.account)
|
return ItemCache(account: self.account)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
public lazy var notices: Notices = {
|
||||||
|
return Notices(account: self.account)
|
||||||
|
}()
|
||||||
|
|
||||||
|
public lazy var preferences: Preferences = {
|
||||||
|
return Preferences(account: self.account)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class TelegramEngineUnauthorized {
|
public final class TelegramEngineUnauthorized {
|
||||||
|
@ -346,12 +346,8 @@ public struct ApplicationSpecificNotice {
|
|||||||
return ApplicationSpecificNoticeKeys.irrelevantPeerGeoNotice(peerId: peerId)
|
return ApplicationSpecificNoticeKeys.irrelevantPeerGeoNotice(peerId: peerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func setIrrelevantPeerGeoReport(postbox: Postbox, peerId: PeerId) -> Signal<Void, NoError> {
|
public static func setIrrelevantPeerGeoReport(engine: TelegramEngine, peerId: PeerId) -> Signal<Never, NoError> {
|
||||||
return postbox.transaction { transaction -> Void in
|
return engine.notices.set(id: ApplicationSpecificNoticeKeys.irrelevantPeerGeoNotice(peerId: peerId), item: ApplicationSpecificBoolNotice())
|
||||||
if let entry = CodableEntry(ApplicationSpecificBoolNotice()) {
|
|
||||||
transaction.setNoticeEntry(key: ApplicationSpecificNoticeKeys.irrelevantPeerGeoNotice(peerId: peerId), value: entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func getBotPaymentLiability(accountManager: AccountManager<TelegramAccountManagerTypes>, peerId: PeerId) -> Signal<Bool, NoError> {
|
public static func getBotPaymentLiability(accountManager: AccountManager<TelegramAccountManagerTypes>, peerId: PeerId) -> Signal<Bool, NoError> {
|
||||||
@ -1107,17 +1103,12 @@ public struct ApplicationSpecificNotice {
|
|||||||
return ApplicationSpecificNoticeKeys.forcedPasswordSetup()
|
return ApplicationSpecificNoticeKeys.forcedPasswordSetup()
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func setForcedPasswordSetup(postbox: Postbox, reloginDaysTimeout: Int32?) -> Signal<Never, NoError> {
|
public static func setForcedPasswordSetup(engine: TelegramEngine, reloginDaysTimeout: Int32?) -> Signal<Never, NoError> {
|
||||||
return postbox.transaction { transaction -> Void in
|
var item: ApplicationSpecificCounterNotice?
|
||||||
if let reloginDaysTimeout = reloginDaysTimeout {
|
if let reloginDaysTimeout = reloginDaysTimeout {
|
||||||
if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: reloginDaysTimeout)) {
|
item = ApplicationSpecificCounterNotice(value: reloginDaysTimeout)
|
||||||
transaction.setNoticeEntry(key: ApplicationSpecificNoticeKeys.forcedPasswordSetup(), value: entry)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
transaction.setNoticeEntry(key: ApplicationSpecificNoticeKeys.forcedPasswordSetup(), value: nil)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|> ignoreValues
|
return engine.notices.set(id: ApplicationSpecificNoticeKeys.forcedPasswordSetup(), item: item)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func reset(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Void, NoError> {
|
public static func reset(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Void, NoError> {
|
||||||
|
@ -63,7 +63,6 @@ swift_library(
|
|||||||
"//submodules/TelegramVoip:TelegramVoip",
|
"//submodules/TelegramVoip:TelegramVoip",
|
||||||
"//submodules/DeviceAccess:DeviceAccess",
|
"//submodules/DeviceAccess:DeviceAccess",
|
||||||
"//submodules/WatchCommon/Host:WatchCommon",
|
"//submodules/WatchCommon/Host:WatchCommon",
|
||||||
"//submodules/LightweightAccountData:LightweightAccountData",
|
|
||||||
"//submodules/BuildConfig:BuildConfig",
|
"//submodules/BuildConfig:BuildConfig",
|
||||||
"//submodules/BuildConfigExtra:BuildConfigExtra",
|
"//submodules/BuildConfigExtra:BuildConfigExtra",
|
||||||
"//submodules/rlottie:RLottieBinding",
|
"//submodules/rlottie:RLottieBinding",
|
||||||
|
@ -29,7 +29,6 @@ import PresentationDataUtils
|
|||||||
import TelegramIntents
|
import TelegramIntents
|
||||||
import AccountUtils
|
import AccountUtils
|
||||||
import CoreSpotlight
|
import CoreSpotlight
|
||||||
import LightweightAccountData
|
|
||||||
import TelegramAudio
|
import TelegramAudio
|
||||||
import DebugSettingsUI
|
import DebugSettingsUI
|
||||||
import BackgroundTasks
|
import BackgroundTasks
|
||||||
@ -775,20 +774,6 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||||||
|
|
||||||
presentationDataPromise.set(sharedContext.presentationData)
|
presentationDataPromise.set(sharedContext.presentationData)
|
||||||
|
|
||||||
let rawAccounts = sharedContext.activeAccountContexts
|
|
||||||
|> map { _, contexts, _ -> [Account] in
|
|
||||||
return contexts.map({ $0.1.account })
|
|
||||||
}
|
|
||||||
let storeQueue = Queue()
|
|
||||||
let _ = (
|
|
||||||
sharedAccountInfos(accountManager: sharedContext.accountManager, accounts: rawAccounts)
|
|
||||||
|> then(Signal<StoredAccountInfos, NoError>.complete() |> delay(10.0, queue: storeQueue))
|
|
||||||
|> restart
|
|
||||||
|> deliverOn(storeQueue)
|
|
||||||
).start(next: { infos in
|
|
||||||
storeAccountsData(rootPath: rootPath, accounts: infos)
|
|
||||||
})
|
|
||||||
|
|
||||||
sharedContext.presentGlobalController = { [weak self] c, a in
|
sharedContext.presentGlobalController = { [weak self] c, a in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -987,24 +972,9 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> mapToSignal { accountAndOtherAccountPhoneNumbers -> Signal<(UnauthorizedAccount, LimitsConfiguration, CallListSettings, ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)]))?, NoError> in
|
|
||||||
return sharedApplicationContext.sharedContext.accountManager.transaction { transaction -> CallListSettings in
|
|
||||||
return transaction.getSharedData(ApplicationSpecificSharedDataKeys.callListSettings)?.get(CallListSettings.self) ?? CallListSettings.defaultSettings
|
|
||||||
}
|
|
||||||
|> mapToSignal { callListSettings -> Signal<(UnauthorizedAccount, LimitsConfiguration, CallListSettings, ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)]))?, NoError> in
|
|
||||||
if let (account, otherAccountPhoneNumbers) = accountAndOtherAccountPhoneNumbers {
|
|
||||||
return account.postbox.transaction { transaction -> (UnauthorizedAccount, LimitsConfiguration, CallListSettings, ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)]))? in
|
|
||||||
let limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration)?.get(LimitsConfiguration.self) ?? LimitsConfiguration.defaultValue
|
|
||||||
return (account, limitsConfiguration, callListSettings, otherAccountPhoneNumbers)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return .single(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> map { accountAndSettings -> UnauthorizedApplicationContext? in
|
|> map { accountAndSettings -> UnauthorizedApplicationContext? in
|
||||||
return accountAndSettings.flatMap { account, limitsConfiguration, callListSettings, otherAccountPhoneNumbers in
|
return accountAndSettings.flatMap { account, otherAccountPhoneNumbers in
|
||||||
return UnauthorizedApplicationContext(apiId: buildConfig.apiId, apiHash: buildConfig.apiHash, sharedContext: sharedApplicationContext.sharedContext, account: account, otherAccountPhoneNumbers: otherAccountPhoneNumbers)
|
return UnauthorizedApplicationContext(apiId: buildConfig.apiId, apiHash: buildConfig.apiHash, sharedContext: sharedApplicationContext.sharedContext, account: account, otherAccountPhoneNumbers: otherAccountPhoneNumbers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3160,52 +3160,49 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}, synchronousLoad: true)
|
}, synchronousLoad: true)
|
||||||
galleryController.setHintWillBePresentedInPreviewingContext(true)
|
galleryController.setHintWillBePresentedInPreviewingContext(true)
|
||||||
|
|
||||||
let items: Signal<[ContextMenuItem], NoError> = context.account.postbox.transaction { transaction -> [ContextMenuItem] in
|
var isChannel = false
|
||||||
var isChannel = false
|
if case let .channel(peer) = peer, case .broadcast = peer.info {
|
||||||
if case let .channel(peer) = peer, case .broadcast = peer.info {
|
isChannel = true
|
||||||
isChannel = true
|
}
|
||||||
}
|
var items: [ContextMenuItem] = [
|
||||||
var items: [ContextMenuItem] = [
|
.action(ContextMenuActionItem(text: isChannel ? strongSelf.presentationData.strings.Conversation_ContextMenuOpenChannelProfile : strongSelf.presentationData.strings.Conversation_ContextMenuOpenProfile, icon: { theme in
|
||||||
.action(ContextMenuActionItem(text: isChannel ? strongSelf.presentationData.strings.Conversation_ContextMenuOpenChannelProfile : strongSelf.presentationData.strings.Conversation_ContextMenuOpenProfile, icon: { theme in
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.actionSheet.primaryTextColor)
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.actionSheet.primaryTextColor)
|
|
||||||
}, action: { _, f in
|
|
||||||
f(.dismissWithoutContent)
|
|
||||||
self?.openPeer(peerId: peer.id, navigation: .info, fromMessage: nil)
|
|
||||||
}))
|
|
||||||
]
|
|
||||||
items.append(.action(ContextMenuActionItem(text: isChannel ? strongSelf.presentationData.strings.Conversation_ContextMenuOpenChannel : strongSelf.presentationData.strings.Conversation_ContextMenuSendMessage, icon: { theme in
|
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: isChannel ? "Chat/Context Menu/Channels" : "Chat/Context Menu/Message"), color: theme.actionSheet.primaryTextColor)
|
|
||||||
}, action: { _, f in
|
}, action: { _, f in
|
||||||
f(.dismissWithoutContent)
|
f(.dismissWithoutContent)
|
||||||
self?.openPeer(peerId: peer.id, navigation: .chat(textInputState: nil, subject: nil, peekData: nil), fromMessage: nil)
|
self?.openPeer(peerId: peer.id, navigation: .info, fromMessage: nil)
|
||||||
})))
|
}))
|
||||||
if !isChannel && canSendMessagesToChat(strongSelf.presentationInterfaceState) {
|
]
|
||||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuMention, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: isChannel ? strongSelf.presentationData.strings.Conversation_ContextMenuOpenChannel : strongSelf.presentationData.strings.Conversation_ContextMenuSendMessage, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Mention"), color: theme.actionSheet.primaryTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: isChannel ? "Chat/Context Menu/Channels" : "Chat/Context Menu/Message"), color: theme.actionSheet.primaryTextColor)
|
||||||
}, action: { _, f in
|
}, action: { _, f in
|
||||||
f(.dismissWithoutContent)
|
f(.dismissWithoutContent)
|
||||||
|
self?.openPeer(peerId: peer.id, navigation: .chat(textInputState: nil, subject: nil, peekData: nil), fromMessage: nil)
|
||||||
guard let strongSelf = self else {
|
})))
|
||||||
return
|
if !isChannel && canSendMessagesToChat(strongSelf.presentationInterfaceState) {
|
||||||
}
|
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuMention, icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Mention"), color: theme.actionSheet.primaryTextColor)
|
||||||
let _ = strongSelf.presentVoiceMessageDiscardAlert(action: {
|
}, action: { _, f in
|
||||||
strongSelf.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
|
f(.dismissWithoutContent)
|
||||||
var inputMode = inputMode
|
|
||||||
if inputMode == .none {
|
guard let strongSelf = self else {
|
||||||
inputMode = .text
|
return
|
||||||
}
|
}
|
||||||
return (chatTextInputAddMentionAttribute(current, peer: peer._asPeer()), inputMode)
|
|
||||||
|
let _ = strongSelf.presentVoiceMessageDiscardAlert(action: {
|
||||||
|
strongSelf.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
|
||||||
|
var inputMode = inputMode
|
||||||
|
if inputMode == .none {
|
||||||
|
inputMode = .text
|
||||||
}
|
}
|
||||||
}, delay: true)
|
return (chatTextInputAddMentionAttribute(current, peer: peer._asPeer()), inputMode)
|
||||||
})))
|
}
|
||||||
}
|
}, delay: true)
|
||||||
return items
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||||
|
|
||||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||||
strongSelf.presentInGlobalOverlay(contextController)
|
strongSelf.presentInGlobalOverlay(contextController)
|
||||||
})
|
})
|
||||||
}, openMessageReplies: { [weak self] messageId, isChannelPost, displayModalProgress in
|
}, openMessageReplies: { [weak self] messageId, isChannelPost, displayModalProgress in
|
||||||
@ -3765,8 +3762,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
onlineMemberCount = recentOnlineSignal
|
onlineMemberCount = recentOnlineSignal
|
||||||
|
|
||||||
self.reportIrrelvantGeoNoticePromise.set(context.account.postbox.transaction { transaction -> Bool? in
|
self.reportIrrelvantGeoNoticePromise.set(context.engine.data.get(TelegramEngine.EngineData.Item.Notices.Notice(key: ApplicationSpecificNotice.irrelevantPeerGeoReportKey(peerId: peerId)))
|
||||||
if let _ = transaction.getNoticeEntry(key: ApplicationSpecificNotice.irrelevantPeerGeoReportKey(peerId: peerId))?.get(ApplicationSpecificBoolNotice.self) {
|
|> map { entry -> Bool? in
|
||||||
|
if let _ = entry?.get(ApplicationSpecificBoolNotice.self) {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -6617,9 +6615,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
let _ = strongSelf.context.engine.privacy.requestUpdatePeerIsBlocked(peerId: peer.id, isBlocked: true).start()
|
let _ = strongSelf.context.engine.privacy.requestUpdatePeerIsBlocked(peerId: peer.id, isBlocked: true).start()
|
||||||
let context = strongSelf.context
|
let context = strongSelf.context
|
||||||
let _ = (context.account.postbox.transaction { transasction -> Void in
|
let _ = context.engine.messages.deleteAllMessagesWithForwardAuthor(peerId: message.id.peerId, forwardAuthorId: peer.id, namespace: Namespaces.Message.Cloud).start()
|
||||||
context.engine.messages.deleteAllMessagesWithForwardAuthor(transaction: transasction, peerId: message.id.peerId, forwardAuthorId: peer.id, namespace: Namespaces.Message.Cloud)
|
|
||||||
}).start()
|
|
||||||
let _ = strongSelf.context.engine.peers.reportRepliesMessage(messageId: message.id, deleteMessage: true, deleteHistory: true, reportSpam: reportSpam).start()
|
let _ = strongSelf.context.engine.peers.reportRepliesMessage(messageId: message.id, deleteMessage: true, deleteHistory: true, reportSpam: reportSpam).start()
|
||||||
})
|
})
|
||||||
] as [ActionSheetItem])
|
] as [ActionSheetItem])
|
||||||
@ -8237,7 +8233,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.reportIrrelvantGeoNoticePromise.set(.single(true))
|
strongSelf.reportIrrelvantGeoNoticePromise.set(.single(true))
|
||||||
let _ = ApplicationSpecificNotice.setIrrelevantPeerGeoReport(postbox: strongSelf.context.account.postbox, peerId: peerId).start()
|
let _ = ApplicationSpecificNotice.setIrrelevantPeerGeoReport(engine: strongSelf.context.engine, peerId: peerId).start()
|
||||||
|
|
||||||
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .emoji(name: "PoliceCar", text: strongSelf.presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), in: .current)
|
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .emoji(name: "PoliceCar", text: strongSelf.presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), in: .current)
|
||||||
}
|
}
|
||||||
@ -11727,11 +11723,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func presentOldMediaPicker(fileMode: Bool, editingMedia: Bool, present: @escaping (AttachmentContainable, AttachmentMediaPickerContext) -> Void, completion: @escaping ([Any], Bool, Int32) -> Void) {
|
private func presentOldMediaPicker(fileMode: Bool, editingMedia: Bool, present: @escaping (AttachmentContainable, AttachmentMediaPickerContext) -> Void, completion: @escaping ([Any], Bool, Int32) -> Void) {
|
||||||
let postbox = self.context.account.postbox
|
let engine = self.context.engine
|
||||||
let _ = (self.context.sharedContext.accountManager.transaction { transaction -> Signal<(GeneratedMediaStoreSettings, SearchBotsConfiguration), NoError> in
|
let _ = (self.context.sharedContext.accountManager.transaction { transaction -> Signal<(GeneratedMediaStoreSettings, EngineConfiguration.SearchBots), NoError> in
|
||||||
let entry = transaction.getSharedData(ApplicationSpecificSharedDataKeys.generatedMediaStoreSettings)?.get(GeneratedMediaStoreSettings.self)
|
let entry = transaction.getSharedData(ApplicationSpecificSharedDataKeys.generatedMediaStoreSettings)?.get(GeneratedMediaStoreSettings.self)
|
||||||
return postbox.transaction { transaction -> (GeneratedMediaStoreSettings, SearchBotsConfiguration) in
|
|
||||||
let configuration = currentSearchBotsConfiguration(transaction: transaction)
|
return engine.data.get(TelegramEngine.EngineData.Item.Configuration.SearchBots())
|
||||||
|
|> map { configuration -> (GeneratedMediaStoreSettings, EngineConfiguration.SearchBots) in
|
||||||
return (entry ?? GeneratedMediaStoreSettings.defaultSettings, configuration)
|
return (entry ?? GeneratedMediaStoreSettings.defaultSettings, configuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11764,7 +11761,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
configureLegacyAssetPicker(controller, context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, initialCaption: inputText, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, presentWebSearch: editingMedia ? nil : { [weak self, weak legacyController] in
|
configureLegacyAssetPicker(controller, context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, initialCaption: inputText, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, presentWebSearch: editingMedia ? nil : { [weak self, weak legacyController] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: EnginePeer(peer), chatLocation: strongSelf.chatLocation, configuration: EngineConfiguration.SearchBots(searchBotsConfiguration), mode: .media(attachment: false, completion: { results, selectionState, editingState, silentPosting in
|
let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: EnginePeer(peer), chatLocation: strongSelf.chatLocation, configuration: searchBotsConfiguration, mode: .media(attachment: false, completion: { results, selectionState, editingState, silentPosting in
|
||||||
if let legacyController = legacyController {
|
if let legacyController = legacyController {
|
||||||
legacyController.dismiss()
|
legacyController.dismiss()
|
||||||
}
|
}
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
import SwiftSignalKit
|
|
||||||
import TelegramCore
|
|
||||||
import Postbox
|
|
||||||
import LightweightAccountData
|
|
||||||
|
|
||||||
private func accountInfo(account: Account) -> Signal<StoredAccountInfo, NoError> {
|
|
||||||
let peerName = account.postbox.transaction { transaction -> String in
|
|
||||||
guard let peer = transaction.getPeer(account.peerId) else {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if let addressName = peer.addressName {
|
|
||||||
return "\(addressName)"
|
|
||||||
}
|
|
||||||
return peer.debugDisplayTitle
|
|
||||||
}
|
|
||||||
|
|
||||||
let primaryDatacenterId = Int32(account.network.datacenterId)
|
|
||||||
let context = account.network.context
|
|
||||||
|
|
||||||
var datacenters: [Int32: AccountDatacenterInfo] = [:]
|
|
||||||
for nId in context.knownDatacenterIds() {
|
|
||||||
if let id = nId as? Int {
|
|
||||||
if let authInfo = context.authInfoForDatacenter(withId: id, selector: .persistent), let authKey = authInfo.authKey {
|
|
||||||
let transportScheme = context.chooseTransportSchemeForConnection(toDatacenterId: id, schemes: context.transportSchemesForDatacenter(withId: id, media: true, enforceMedia: false, isProxy: false))
|
|
||||||
var addressList: [AccountDatacenterAddress] = []
|
|
||||||
if let transportScheme = transportScheme, let host = transportScheme.address.host {
|
|
||||||
let secret: Data? = transportScheme.address.secret
|
|
||||||
addressList.append(AccountDatacenterAddress(host: host, port: Int32(transportScheme.address.port), isMedia: transportScheme.address.preferForMedia, secret: secret))
|
|
||||||
}
|
|
||||||
|
|
||||||
var ephemeralMainKey: AccountDatacenterKey?
|
|
||||||
if let ephemeralMainAuthInfo = context.authInfoForDatacenter(withId: id, selector: .ephemeralMain), let ephemeralAuthKey = ephemeralMainAuthInfo.authKey {
|
|
||||||
ephemeralMainKey = AccountDatacenterKey(id: ephemeralMainAuthInfo.authKeyId, data: ephemeralAuthKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
var ephemeralMediaKey: AccountDatacenterKey?
|
|
||||||
if let ephemeralMediaAuthInfo = context.authInfoForDatacenter(withId: id, selector: .ephemeralMedia), let ephemeralAuthKey = ephemeralMediaAuthInfo.authKey {
|
|
||||||
ephemeralMediaKey = AccountDatacenterKey(id: ephemeralMediaAuthInfo.authKeyId, data: ephemeralAuthKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
datacenters[Int32(id)] = AccountDatacenterInfo(
|
|
||||||
masterKey: AccountDatacenterKey(id: authInfo.authKeyId, data: authKey),
|
|
||||||
ephemeralMainKey: ephemeralMainKey,
|
|
||||||
ephemeralMediaKey: ephemeralMediaKey,
|
|
||||||
addressList: addressList
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let notificationKey = masterNotificationsKey(account: account, ignoreDisabled: false)
|
|
||||||
|
|
||||||
return combineLatest(peerName, notificationKey)
|
|
||||||
|> map { peerName, notificationKey -> StoredAccountInfo in
|
|
||||||
return StoredAccountInfo(
|
|
||||||
id: account.id.int64,
|
|
||||||
primaryId: primaryDatacenterId,
|
|
||||||
isTestingEnvironment: account.testingEnvironment,
|
|
||||||
peerName: peerName,
|
|
||||||
datacenters: datacenters,
|
|
||||||
notificationKey: AccountNotificationKey(id: notificationKey.id, data: notificationKey.data)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sharedAccountInfos(accountManager: AccountManager<TelegramAccountManagerTypes>, accounts: Signal<[Account], NoError>) -> Signal<StoredAccountInfos, NoError> {
|
|
||||||
return combineLatest(accountManager.sharedData(keys: [SharedDataKeys.proxySettings]), accounts)
|
|
||||||
|> take(1)
|
|
||||||
|> mapToSignal { sharedData, accounts -> Signal<StoredAccountInfos, NoError> in
|
|
||||||
let proxySettings = sharedData.entries[SharedDataKeys.proxySettings]?.get(ProxySettings.self)
|
|
||||||
let proxy = proxySettings?.effectiveActiveServer.flatMap { proxyServer -> AccountProxyConnection? in
|
|
||||||
var username: String?
|
|
||||||
var password: String?
|
|
||||||
var secret: Data?
|
|
||||||
switch proxyServer.connection {
|
|
||||||
case let .socks5(usernameValue, passwordValue):
|
|
||||||
username = usernameValue
|
|
||||||
password = passwordValue
|
|
||||||
case let .mtp(secretValue):
|
|
||||||
secret = secretValue
|
|
||||||
}
|
|
||||||
return AccountProxyConnection(host: proxyServer.host, port: proxyServer.port, username: username, password: password, secret: secret)
|
|
||||||
}
|
|
||||||
|
|
||||||
return combineLatest(accounts.map(accountInfo))
|
|
||||||
|> map { infos -> StoredAccountInfos in
|
|
||||||
return StoredAccountInfos(proxy: proxy, accounts: infos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func storeAccountsData(rootPath: String, accounts: StoredAccountInfos) {
|
|
||||||
guard let data = try? JSONEncoder().encode(accounts) else {
|
|
||||||
Logger.shared.log("storeAccountsData", "Error encoding data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard let _ = try? data.write(to: URL(fileURLWithPath: rootPath + "/accounts-shared-data")) else {
|
|
||||||
Logger.shared.log("storeAccountsData", "Error saving data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
@ -35,8 +35,8 @@ public struct ChatArchiveSettings: Equatable, Codable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateChatArchiveSettings(transaction: Transaction, _ f: @escaping (ChatArchiveSettings) -> ChatArchiveSettings) {
|
public func updateChatArchiveSettings(engine: TelegramEngine, _ f: @escaping (ChatArchiveSettings) -> ChatArchiveSettings) -> Signal<Never, NoError> {
|
||||||
transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.chatArchiveSettings, { entry in
|
return engine.preferences.update(id: ApplicationSpecificPreferencesKeys.chatArchiveSettings, { entry in
|
||||||
let currentSettings: ChatArchiveSettings
|
let currentSettings: ChatArchiveSettings
|
||||||
if let entry = entry?.get(ChatArchiveSettings.self) {
|
if let entry = entry?.get(ChatArchiveSettings.self) {
|
||||||
currentSettings = entry
|
currentSettings = entry
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
import Postbox
|
|
||||||
import TelegramCore
|
|
||||||
import SwiftSignalKit
|
|
||||||
|
|
||||||
public struct WidgetSettings: Codable, Equatable {
|
|
||||||
public var useHints: Bool
|
|
||||||
public var peers: [PeerId]
|
|
||||||
|
|
||||||
public static var `default`: WidgetSettings {
|
|
||||||
return WidgetSettings(
|
|
||||||
useHints: true,
|
|
||||||
peers: []
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(
|
|
||||||
useHints: Bool,
|
|
||||||
peers: [PeerId]
|
|
||||||
) {
|
|
||||||
self.useHints = useHints
|
|
||||||
self.peers = peers
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
|
||||||
let container = try decoder.container(keyedBy: StringCodingKey.self)
|
|
||||||
|
|
||||||
self.useHints = try container.decode(Bool.self, forKey: "useHints")
|
|
||||||
self.peers = (try container.decode([Int64].self, forKey: "peers")).map { PeerId($0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
|
||||||
var container = encoder.container(keyedBy: StringCodingKey.self)
|
|
||||||
|
|
||||||
try container.encode(self.useHints, forKey: "useHints")
|
|
||||||
try container.encode(self.peers.map { $0.toInt64() } as [Int64], forKey: "peers")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func updateWidgetSettingsInteractively(postbox: Postbox, _ f: @escaping (WidgetSettings) -> WidgetSettings) -> Signal<Never, NoError> {
|
|
||||||
return postbox.transaction { transaction -> Void in
|
|
||||||
updateWidgetSettingsInteractively(transaction: transaction, f)
|
|
||||||
}
|
|
||||||
|> ignoreValues
|
|
||||||
}
|
|
||||||
|
|
||||||
public func updateWidgetSettingsInteractively(transaction: Transaction, _ f: @escaping (WidgetSettings) -> WidgetSettings) {
|
|
||||||
transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.widgetSettings, { entry in
|
|
||||||
let currentSettings: WidgetSettings
|
|
||||||
if let entry = entry?.get(WidgetSettings.self) {
|
|
||||||
currentSettings = entry
|
|
||||||
} else {
|
|
||||||
currentSettings = .default
|
|
||||||
}
|
|
||||||
return PreferencesEntry(f(currentSettings))
|
|
||||||
})
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user