mirror of
https://github.com/danog/Telegram.git
synced 2024-12-02 09:27:55 +01:00
987 lines
34 KiB
Objective-C
987 lines
34 KiB
Objective-C
#import "TGConversation.h"
|
|
|
|
#import "TGMessage.h"
|
|
|
|
#import "PSKeyValueCoder.h"
|
|
|
|
@implementation TGEncryptedConversationData
|
|
|
|
- (BOOL)isEqualToEncryptedData:(TGEncryptedConversationData *)other
|
|
{
|
|
if (_encryptedConversationId != other->_encryptedConversationId || _accessHash != other->_accessHash || _keyFingerprint != other->_keyFingerprint || _handshakeState != other->_handshakeState || _currentRekeyExchangeId != other->_currentRekeyExchangeId)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
- (id)copyWithZone:(NSZone *)__unused zone
|
|
{
|
|
TGEncryptedConversationData *data = [[TGEncryptedConversationData alloc] init];
|
|
data->_encryptedConversationId = _encryptedConversationId;
|
|
data->_accessHash = _accessHash;
|
|
data->_keyFingerprint = _keyFingerprint;
|
|
data->_handshakeState = _handshakeState;
|
|
data->_currentRekeyExchangeId = _currentRekeyExchangeId;
|
|
data->_currentRekeyIsInitiatedByLocalClient = _currentRekeyIsInitiatedByLocalClient;
|
|
data->_currentRekeyNumber = _currentRekeyNumber;
|
|
data->_currentRekeyKey = _currentRekeyKey;
|
|
data->_currentRekeyKeyId = _currentRekeyKeyId;
|
|
|
|
return data;
|
|
}
|
|
|
|
- (void)serialize:(NSMutableData *)data
|
|
{
|
|
uint8_t version = 3;
|
|
[data appendBytes:&version length:1];
|
|
[data appendBytes:&_encryptedConversationId length:8];
|
|
[data appendBytes:&_accessHash length:8];
|
|
[data appendBytes:&_keyFingerprint length:8];
|
|
[data appendBytes:&_handshakeState length:4];
|
|
[data appendBytes:&_currentRekeyExchangeId length:8];
|
|
uint8_t currentRekeyIsInitiatedByLocalClient = _currentRekeyIsInitiatedByLocalClient ? 1 : 0;
|
|
[data appendBytes:¤tRekeyIsInitiatedByLocalClient length:1];
|
|
int32_t currentRekeyNumberLength = (int32_t)_currentRekeyNumber.length;
|
|
[data appendBytes:¤tRekeyNumberLength length:4];
|
|
if (_currentRekeyNumber != nil)
|
|
[data appendData:_currentRekeyNumber];
|
|
int32_t currentRekeyKeyLength = (int32_t)_currentRekeyKey.length;
|
|
[data appendBytes:¤tRekeyKeyLength length:4];
|
|
if (_currentRekeyKey != nil)
|
|
[data appendData:_currentRekeyKey];
|
|
[data appendBytes:&_currentRekeyKeyId length:8];
|
|
}
|
|
|
|
+ (TGEncryptedConversationData *)deserialize:(NSData *)data ptr:(int *)ptr
|
|
{
|
|
uint8_t version = 0;
|
|
[data getBytes:&version range:NSMakeRange(*ptr, 1)];
|
|
(*ptr) += 1;
|
|
|
|
if (version != 1 && version != 2 && version != 3)
|
|
{
|
|
TGLog(@"***** Invalid encrypted data version");
|
|
return nil;
|
|
}
|
|
|
|
TGEncryptedConversationData *encryptedData = [TGEncryptedConversationData new];
|
|
|
|
[data getBytes:&encryptedData->_encryptedConversationId range:NSMakeRange(*ptr, 8)];
|
|
(*ptr) += 8;
|
|
|
|
[data getBytes:&encryptedData->_accessHash range:NSMakeRange(*ptr, 8)];
|
|
(*ptr) += 8;
|
|
|
|
[data getBytes:&encryptedData->_keyFingerprint range:NSMakeRange(*ptr, 8)];
|
|
(*ptr) += 8;
|
|
|
|
if (version >= 2)
|
|
{
|
|
[data getBytes:&encryptedData->_handshakeState range:NSMakeRange(*ptr, 4)];
|
|
*ptr += 4;
|
|
}
|
|
|
|
if (version >= 3)
|
|
{
|
|
[data getBytes:&encryptedData->_currentRekeyExchangeId range:NSMakeRange(*ptr, 8)];
|
|
*ptr += 8;
|
|
|
|
uint8_t currentRekeyIsInitiatedByLocalClient = 0;
|
|
[data getBytes:¤tRekeyIsInitiatedByLocalClient range:NSMakeRange(*ptr, 1)];
|
|
encryptedData->_currentRekeyIsInitiatedByLocalClient = currentRekeyIsInitiatedByLocalClient;
|
|
*ptr += 1;
|
|
|
|
int32_t currentRekeyNumberLength = 0;
|
|
[data getBytes:¤tRekeyNumberLength range:NSMakeRange(*ptr, 4)];
|
|
*ptr += 4;
|
|
|
|
if (currentRekeyNumberLength != 0)
|
|
{
|
|
encryptedData->_currentRekeyNumber = [data subdataWithRange:NSMakeRange(*ptr, currentRekeyNumberLength)];
|
|
*ptr += currentRekeyNumberLength;
|
|
}
|
|
|
|
int32_t currentRekeyKeyLength = 0;
|
|
[data getBytes:¤tRekeyKeyLength range:NSMakeRange(*ptr, 4)];
|
|
*ptr += 4;
|
|
|
|
if (currentRekeyKeyLength != 0)
|
|
{
|
|
encryptedData->_currentRekeyKey = [data subdataWithRange:NSMakeRange(*ptr, currentRekeyKeyLength)];
|
|
*ptr += currentRekeyKeyLength;
|
|
}
|
|
|
|
[data getBytes:&encryptedData->_currentRekeyKeyId range:NSMakeRange(*ptr, 8)];
|
|
*ptr += 8;
|
|
}
|
|
|
|
return encryptedData;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation TGConversationParticipantsData
|
|
|
|
- (id)init
|
|
{
|
|
self = [super init];
|
|
if (self != nil)
|
|
{
|
|
_serializedData = nil;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (id)copyWithZone:(NSZone *)__unused zone
|
|
{
|
|
TGConversationParticipantsData *participantsData = [[TGConversationParticipantsData alloc] init];
|
|
|
|
participantsData.chatAdminId = _chatAdminId;
|
|
participantsData.chatInvitedBy = _chatInvitedBy;
|
|
participantsData.chatInvitedDates = _chatInvitedDates;
|
|
participantsData.chatParticipantUids = _chatParticipantUids;
|
|
participantsData.chatParticipantSecretChatPeerIds = _chatParticipantSecretChatPeerIds;
|
|
participantsData.chatParticipantChatPeerIds = _chatParticipantChatPeerIds;
|
|
participantsData.chatAdminUids = _chatAdminUids;
|
|
participantsData.version = _version;
|
|
participantsData.exportedChatInviteString = _exportedChatInviteString;
|
|
|
|
return participantsData;
|
|
}
|
|
|
|
- (void)addParticipantWithId:(int32_t)uid invitedBy:(int32_t)invitedBy date:(int32_t)date
|
|
{
|
|
NSMutableArray *chatParticipantUids = [[NSMutableArray alloc] initWithArray:_chatParticipantUids];
|
|
if (![chatParticipantUids containsObject:@(uid)])
|
|
{
|
|
[chatParticipantUids addObject:@(uid)];
|
|
_chatParticipantUids = chatParticipantUids;
|
|
|
|
NSMutableDictionary *chatInvitedBy = [[NSMutableDictionary alloc] initWithDictionary:_chatInvitedBy];
|
|
chatInvitedBy[@(uid)] = @(invitedBy);
|
|
_chatInvitedBy = chatInvitedBy;
|
|
|
|
NSMutableDictionary *chatInvitedDates = [[NSMutableDictionary alloc] initWithDictionary:_chatInvitedDates];
|
|
chatInvitedDates[@(uid)] = @(date);
|
|
_chatInvitedDates = chatInvitedDates;
|
|
}
|
|
}
|
|
|
|
- (void)removeParticipantWithId:(int32_t)uid
|
|
{
|
|
NSMutableArray *chatParticipantUids = [[NSMutableArray alloc] initWithArray:_chatParticipantUids];
|
|
[chatParticipantUids removeObject:@(uid)];
|
|
_chatParticipantUids = chatParticipantUids;
|
|
|
|
NSMutableDictionary *chatInvitedBy = [[NSMutableDictionary alloc] initWithDictionary:_chatInvitedBy];
|
|
[chatInvitedBy removeObjectForKey:@(uid)];
|
|
_chatInvitedBy = chatInvitedBy;
|
|
|
|
NSMutableDictionary *chatInvitedDates = [[NSMutableDictionary alloc] initWithDictionary:_chatInvitedDates];
|
|
[chatInvitedDates removeObjectForKey:@(uid)];
|
|
_chatInvitedDates = chatInvitedDates;
|
|
|
|
NSMutableSet *chatAdminUids = [[NSMutableSet alloc] initWithSet:_chatAdminUids];
|
|
[chatAdminUids removeObject:@(uid)];
|
|
_chatAdminUids = chatAdminUids;
|
|
}
|
|
|
|
- (void)addSecretChatPeerWithId:(int64_t)peerId
|
|
{
|
|
NSMutableArray *chatParticipantSecretChatPeerIds = [[NSMutableArray alloc] initWithArray:_chatParticipantSecretChatPeerIds];
|
|
if (![chatParticipantSecretChatPeerIds containsObject:@(peerId)])
|
|
{
|
|
[chatParticipantSecretChatPeerIds addObject:@(peerId)];
|
|
_chatParticipantSecretChatPeerIds = chatParticipantSecretChatPeerIds;
|
|
}
|
|
}
|
|
|
|
- (void)removeSecretChatPeerWithId:(int64_t)peerId
|
|
{
|
|
NSMutableArray *chatParticipantSecretChatPeerIds = [[NSMutableArray alloc] initWithArray:_chatParticipantSecretChatPeerIds];
|
|
[chatParticipantSecretChatPeerIds removeObject:@(peerId)];
|
|
_chatParticipantSecretChatPeerIds = chatParticipantSecretChatPeerIds;
|
|
}
|
|
|
|
- (void)addChatPeerWithId:(int64_t)peerId
|
|
{
|
|
NSMutableArray *chatParticipantChatPeerIds = [[NSMutableArray alloc] initWithArray:_chatParticipantChatPeerIds];
|
|
if (![chatParticipantChatPeerIds containsObject:@(peerId)])
|
|
{
|
|
[chatParticipantChatPeerIds addObject:@(peerId)];
|
|
_chatParticipantChatPeerIds = chatParticipantChatPeerIds;
|
|
}
|
|
}
|
|
|
|
- (void)removeChatPeerWithId:(int64_t)peerId
|
|
{
|
|
NSMutableArray *chatParticipantChatPeerIds = [[NSMutableArray alloc] initWithArray:_chatParticipantChatPeerIds];
|
|
[chatParticipantChatPeerIds removeObject:@(peerId)];
|
|
_chatParticipantChatPeerIds = chatParticipantChatPeerIds;
|
|
}
|
|
|
|
+ (TGConversationParticipantsData *)deserializeData:(NSData *)data
|
|
{
|
|
TGConversationParticipantsData *participantsData = [[TGConversationParticipantsData alloc] init];
|
|
|
|
int length = (int)data.length;
|
|
int ptr = 0;
|
|
if (ptr + 12 > length)
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
int version = 0;
|
|
[data getBytes:&version range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
|
|
int32_t formatVersion = 0;
|
|
if (version == (int)0xabcdef12)
|
|
{
|
|
[data getBytes:&formatVersion range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
|
|
[data getBytes:&version range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
}
|
|
|
|
int adminId = 0;
|
|
[data getBytes:&adminId range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
|
|
int count = 0;
|
|
[data getBytes:&count range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
|
|
NSMutableArray *uids = [[NSMutableArray alloc] init];
|
|
NSMutableDictionary *invitedBy = [[NSMutableDictionary alloc] init];
|
|
NSMutableDictionary *invitedDates = [[NSMutableDictionary alloc] init];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (ptr + 4 > length)
|
|
{
|
|
TGLog(@"***** Invalid participants data");
|
|
return nil;
|
|
}
|
|
|
|
int uid = 0;
|
|
[data getBytes:&uid range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
|
|
if (ptr + 4 > length)
|
|
{
|
|
TGLog(@"***** Invalid participants data");
|
|
return nil;
|
|
}
|
|
int inviter = 0;
|
|
[data getBytes:&inviter range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
|
|
if (ptr + 4 > length)
|
|
{
|
|
TGLog(@"***** Invalid participants data");
|
|
return nil;
|
|
}
|
|
int date = 0;
|
|
[data getBytes:&date range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
|
|
NSNumber *nUid = [[NSNumber alloc] initWithInt:uid];
|
|
|
|
[uids addObject:nUid];
|
|
[invitedBy setObject:[[NSNumber alloc] initWithInt:inviter] forKey:nUid];
|
|
[invitedDates setObject:[[NSNumber alloc] initWithInt:date] forKey:nUid];
|
|
}
|
|
|
|
NSMutableArray *chatParticipantSecretChatPeerIds = [[NSMutableArray alloc] init];
|
|
|
|
if (formatVersion >= 1)
|
|
{
|
|
int count = 0;
|
|
[data getBytes:&count range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (ptr + 8 > length)
|
|
{
|
|
TGLog(@"***** Invalid participants data");
|
|
return nil;
|
|
}
|
|
|
|
int64_t peerId = 0;
|
|
[data getBytes:&peerId range:NSMakeRange(ptr, 8)];
|
|
ptr += 8;
|
|
|
|
[chatParticipantSecretChatPeerIds addObject:@(peerId)];
|
|
}
|
|
}
|
|
|
|
NSMutableArray *chatParticipantChatPeerIds = [[NSMutableArray alloc] init];
|
|
|
|
if (formatVersion >= 2)
|
|
{
|
|
int count = 0;
|
|
[data getBytes:&count range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (ptr + 8 > length)
|
|
{
|
|
TGLog(@"***** Invalid participants data");
|
|
return nil;
|
|
}
|
|
|
|
int64_t peerId = 0;
|
|
[data getBytes:&peerId range:NSMakeRange(ptr, 8)];
|
|
ptr += 8;
|
|
|
|
[chatParticipantChatPeerIds addObject:@(peerId)];
|
|
}
|
|
}
|
|
|
|
if (formatVersion >= 3)
|
|
{
|
|
int32_t length = 0;
|
|
[data getBytes:&length range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
|
|
NSData *linkData = [data subdataWithRange:NSMakeRange(ptr, length)];
|
|
ptr += length;
|
|
|
|
participantsData.exportedChatInviteString = [[NSString alloc] initWithData:linkData encoding:NSUTF8StringEncoding];
|
|
}
|
|
|
|
if (formatVersion >= 4) {
|
|
int32_t length = 0;
|
|
[data getBytes:&length range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
|
|
NSMutableSet *chatAdminUids = [[NSMutableSet alloc] init];
|
|
for (int32_t i = 0; i < length; i++) {
|
|
int32_t item = 0;
|
|
[data getBytes:&item range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
[chatAdminUids addObject:@(item)];
|
|
}
|
|
|
|
participantsData.chatAdminUids = chatAdminUids;
|
|
}
|
|
|
|
participantsData.version = version;
|
|
participantsData.chatAdminId = adminId;
|
|
participantsData.chatParticipantUids = uids;
|
|
participantsData.chatInvitedBy = invitedBy;
|
|
participantsData.chatInvitedDates = invitedDates;
|
|
participantsData.chatParticipantSecretChatPeerIds = chatParticipantSecretChatPeerIds;
|
|
participantsData.chatParticipantChatPeerIds = chatParticipantChatPeerIds;
|
|
|
|
return participantsData;
|
|
}
|
|
|
|
- (NSData *)serializedData
|
|
{
|
|
if (_serializedData == nil)
|
|
{
|
|
NSMutableData *data = [[NSMutableData alloc] init];
|
|
|
|
int32_t magic = 0xabcdef12;
|
|
[data appendBytes:&magic length:4];
|
|
|
|
int32_t formatVersion = 4;
|
|
[data appendBytes:&formatVersion length:4];
|
|
|
|
[data appendBytes:&_version length:4];
|
|
[data appendBytes:&_chatAdminId length:4];
|
|
|
|
int count = (int)_chatParticipantUids.count;
|
|
[data appendBytes:&count length:4];
|
|
for (NSNumber *nUid in _chatParticipantUids)
|
|
{
|
|
int uid = [nUid intValue];
|
|
[data appendBytes:&uid length:4];
|
|
|
|
int invitedBy = [[_chatInvitedBy objectForKey:nUid] intValue];
|
|
[data appendBytes:&invitedBy length:4];
|
|
|
|
int invitedDate = [[_chatInvitedDates objectForKey:nUid] intValue];
|
|
[data appendBytes:&invitedDate length:4];
|
|
}
|
|
|
|
int32_t chatParticipantSecretChatPeerIdsCount = (int32_t)_chatParticipantSecretChatPeerIds.count;
|
|
[data appendBytes:&chatParticipantSecretChatPeerIdsCount length:4];
|
|
|
|
for (NSNumber *nPeerId in _chatParticipantSecretChatPeerIds)
|
|
{
|
|
int64_t peerId = [nPeerId longLongValue];
|
|
[data appendBytes:&peerId length:8];
|
|
}
|
|
|
|
int32_t chatParticipantChatPeerIdsCount = (int32_t)_chatParticipantChatPeerIds.count;
|
|
[data appendBytes:&chatParticipantChatPeerIdsCount length:4];
|
|
|
|
for (NSNumber *nPeerId in _chatParticipantChatPeerIds)
|
|
{
|
|
int64_t peerId = [nPeerId longLongValue];
|
|
[data appendBytes:&peerId length:8];
|
|
}
|
|
|
|
int32_t linkLength = (int32_t)_exportedChatInviteString.length;
|
|
[data appendBytes:&linkLength length:4];
|
|
if (linkLength != 0)
|
|
[data appendData:[_exportedChatInviteString dataUsingEncoding:NSUTF8StringEncoding]];
|
|
|
|
int32_t chatAdminUidsCount = (int32_t)_chatAdminUids.count;
|
|
[data appendBytes:&chatAdminUidsCount length:4];
|
|
for (NSNumber *nUid in _chatAdminUids) {
|
|
int32_t uid = [nUid intValue];
|
|
[data appendBytes:&uid length:4];
|
|
}
|
|
|
|
_serializedData = data;
|
|
}
|
|
|
|
return _serializedData;
|
|
}
|
|
|
|
@end
|
|
|
|
#pragma mark -
|
|
|
|
@implementation TGConversation
|
|
|
|
- (id)initWithConversationId:(int64_t)conversationId unreadCount:(int)unreadCount serviceUnreadCount:(int)serviceUnreadCount
|
|
{
|
|
self = [super init];
|
|
if (self != nil)
|
|
{
|
|
_conversationId = conversationId;
|
|
_unreadCount = unreadCount;
|
|
_serviceUnreadCount = serviceUnreadCount;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder
|
|
{
|
|
self = [super init];
|
|
if (self != nil) {
|
|
_conversationId = [coder decodeInt64ForCKey:"i"];
|
|
_accessHash = [coder decodeInt64ForCKey:"ah"];
|
|
_displayVariant = [coder decodeInt32ForCKey:"dv"];
|
|
_kind = (uint8_t)[coder decodeInt32ForCKey:"kind"];
|
|
_pts = [coder decodeInt32ForCKey:"pts"];
|
|
_variantSortKey = TGConversationSortKeyDecode(coder, "vsort");
|
|
_importantSortKey = TGConversationSortKeyDecode(coder, "isort");
|
|
_unimportantSortKey = TGConversationSortKeyDecode(coder, "usort");
|
|
_maxReadMessageId = [coder decodeInt32ForCKey:"mread"];
|
|
_about = [coder decodeStringForCKey:"about"];
|
|
_username = [coder decodeStringForCKey:"username"];
|
|
_outgoing = [coder decodeInt32ForCKey:"out"];
|
|
_unread = [coder decodeInt32ForCKey:"unr"];
|
|
_deliveryError = [coder decodeInt32ForCKey:"der"];
|
|
_deliveryState = [coder decodeInt32ForCKey:"ds"];
|
|
_date = [coder decodeInt32ForCKey:"date"];
|
|
_fromUid = [coder decodeInt32ForCKey:"from"];
|
|
_text = [coder decodeStringForCKey:"text"];
|
|
_media = [TGMessage parseMediaAttachments:[coder decodeDataCorCKey:"media"]];
|
|
_unreadCount = [coder decodeInt32ForCKey:"ucount"];
|
|
_serviceUnreadCount = [coder decodeInt32ForCKey:"sucount"];
|
|
_chatTitle = [coder decodeStringForCKey:"ct"];
|
|
_chatPhotoSmall = [coder decodeStringForCKey:"cp.s"];
|
|
_chatPhotoMedium = [coder decodeStringForCKey:"cp.m"];
|
|
_chatPhotoBig = [coder decodeStringForCKey:"cp.l"];
|
|
_chatParticipants = nil;
|
|
_chatParticipantCount = 0;
|
|
_chatVersion = [coder decodeInt32ForCKey:"ver"];
|
|
_chatIsAdmin = [coder decodeInt32ForCKey:"adm"];
|
|
_channelRole = [coder decodeInt32ForCKey:"role"];
|
|
_channelIsReadOnly = [coder decodeInt32ForCKey:"ro"];
|
|
_flags = [coder decodeInt64ForCKey:"flags"];
|
|
_leftChat = false;
|
|
_kickedFromChat = [coder decodeInt32ForCKey:"kk"];
|
|
_isChat = false;
|
|
_isChannel = true;
|
|
_isDeleted = false;
|
|
_encryptedData = nil;
|
|
_isBroadcast = false;
|
|
_migratedToChannelId = [coder decodeInt32ForCKey:"mtci"];
|
|
_migratedToChannelAccessHash = [coder decodeInt64ForCKey:"mtch"];
|
|
_restrictionReason = [coder decodeStringForCKey:"rr"];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder {
|
|
[coder encodeInt64:_conversationId forCKey:"i"];
|
|
[coder encodeInt64:_accessHash forCKey:"ah"];
|
|
[coder encodeInt32:_displayVariant forCKey:"dv"];
|
|
[coder encodeInt32:_kind forCKey:"kind"];
|
|
[coder encodeInt32:_pts forCKey:"pts"];
|
|
TGConversationSortKeyEncode(coder, "vsort", _variantSortKey);
|
|
TGConversationSortKeyEncode(coder, "isort", _importantSortKey);
|
|
TGConversationSortKeyEncode(coder, "usort", _unimportantSortKey);
|
|
[coder encodeInt32:_maxReadMessageId forCKey:"mread"];
|
|
[coder encodeString:_about forCKey:"about"];
|
|
[coder encodeString:_username forCKey:"username"];
|
|
[coder encodeInt32:_outgoing ? 1 : 0 forCKey:"out"];
|
|
[coder encodeInt32:_unread ? 1 : 0 forCKey:"unr"];
|
|
[coder encodeInt32:_deliveryError ? 1 : 0 forCKey:"der"];
|
|
[coder encodeInt32:_deliveryState forCKey:"ds"];
|
|
[coder encodeInt32:_date forCKey:"date"];
|
|
[coder encodeInt32:_fromUid forCKey:"from"];
|
|
[coder encodeString:_text forCKey:"text"];
|
|
[coder encodeData:[TGMessage serializeMediaAttachments:true attachments:_media] forCKey:"media"];
|
|
[coder encodeInt32:_unreadCount forCKey:"ucount"];
|
|
[coder encodeInt32:_serviceUnreadCount forCKey:"sucount"];
|
|
[coder encodeString:_chatTitle forCKey:"ct"];
|
|
[coder encodeString:_chatPhotoSmall forCKey:"cp.s"];
|
|
[coder encodeString:_chatPhotoMedium forCKey:"cp.m"];
|
|
[coder encodeString:_chatPhotoBig forCKey:"cp.l"];
|
|
[coder encodeInt32:_chatVersion forCKey:"ver"];
|
|
[coder encodeInt32:_chatIsAdmin ? 1 : 0 forCKey:"adm"];
|
|
[coder encodeInt32:_channelRole forCKey:"role"];
|
|
[coder encodeInt32:_channelIsReadOnly ? 1 : 0 forCKey:"ro"];
|
|
[coder encodeInt64:_flags forCKey:"flags"];
|
|
[coder encodeInt32:_kickedFromChat forCKey:"kk"];
|
|
[coder encodeInt32:_migratedToChannelId forCKey:"mtci"];
|
|
[coder encodeInt64:_migratedToChannelAccessHash forCKey:"mtch"];
|
|
[coder encodeString:_restrictionReason forCKey:"rr"];
|
|
}
|
|
|
|
- (id)copyWithZone:(NSZone *)__unused zone
|
|
{
|
|
TGConversation *conversation = [[TGConversation alloc] init];
|
|
|
|
conversation.conversationId = _conversationId;
|
|
conversation.accessHash = _accessHash;
|
|
conversation.displayVariant = _displayVariant;
|
|
conversation->_kind = _kind;
|
|
conversation.pts = _pts;
|
|
conversation.variantSortKey = _variantSortKey;
|
|
conversation.importantSortKey = _importantSortKey;
|
|
conversation.unimportantSortKey = _unimportantSortKey;
|
|
conversation.maxReadMessageId = _maxReadMessageId;
|
|
conversation.about = _about;
|
|
conversation.username = _username;
|
|
conversation.outgoing = _outgoing;
|
|
conversation.unread = _unread;
|
|
conversation.deliveryError = _deliveryError;
|
|
conversation.deliveryState = _deliveryState;
|
|
conversation.date = _date;
|
|
conversation.fromUid = _fromUid;
|
|
conversation.text = _text;
|
|
conversation.media = _media;
|
|
conversation.unreadCount = _unreadCount;
|
|
conversation.serviceUnreadCount = _serviceUnreadCount;
|
|
conversation.chatTitle = _chatTitle;
|
|
conversation.chatPhotoSmall = _chatPhotoSmall;
|
|
conversation.chatPhotoMedium = _chatPhotoMedium;
|
|
conversation.chatPhotoBig = _chatPhotoBig;
|
|
conversation.chatParticipants = [_chatParticipants copy];
|
|
conversation.chatParticipantCount = _chatParticipantCount;
|
|
conversation.chatVersion = _chatVersion;
|
|
conversation.chatIsAdmin = _chatIsAdmin;
|
|
conversation.channelRole = _channelRole;
|
|
conversation.leftChat = _leftChat;
|
|
conversation.kickedFromChat = _kickedFromChat;
|
|
conversation.dialogListData = _dialogListData;
|
|
conversation.isChat = _isChat;
|
|
conversation.isDeleted = _isDeleted;
|
|
conversation.restrictionReason = _restrictionReason;
|
|
|
|
conversation.encryptedData = _encryptedData == nil ? nil : [_encryptedData copy];
|
|
|
|
conversation.isBroadcast = _isBroadcast;
|
|
conversation.isChannel = _isChannel;
|
|
conversation.channelIsReadOnly = _channelIsReadOnly;
|
|
conversation.flags = _flags;
|
|
conversation.migratedToChannelId = _migratedToChannelId;
|
|
conversation.migratedToChannelAccessHash = _migratedToChannelAccessHash;
|
|
|
|
return conversation;
|
|
}
|
|
|
|
- (void)setKind:(uint8_t)kind {
|
|
if (_kind != kind || kind != TGConversationSortKeyKind(_variantSortKey)) {
|
|
_kind = kind;
|
|
|
|
_variantSortKey = TGConversationSortKeyUpdateKind(_variantSortKey, kind);
|
|
_importantSortKey = TGConversationSortKeyUpdateKind(_importantSortKey, kind);
|
|
_unimportantSortKey = TGConversationSortKeyUpdateKind(_unimportantSortKey, kind);
|
|
}
|
|
}
|
|
|
|
- (void)setVariantSortKey:(TGConversationSortKey)variantSortKey {
|
|
_variantSortKey = variantSortKey;
|
|
|
|
_date = TGConversationSortKeyTimestamp(variantSortKey);
|
|
}
|
|
|
|
- (void)mergeMessage:(TGMessage *)message
|
|
{
|
|
_outgoing = message.outgoing;
|
|
_date = (int)message.date;
|
|
_fromUid = (int)message.fromUid;
|
|
_text = message.text;
|
|
_media = message.mediaAttachments;
|
|
_unread = message.unread;
|
|
_deliveryError = message.deliveryState == TGMessageDeliveryStateFailed;
|
|
_deliveryState = message.deliveryState;
|
|
}
|
|
|
|
- (NSData *)mediaData
|
|
{
|
|
if (_mediaData != nil)
|
|
return _mediaData;
|
|
|
|
_mediaData = [TGMessage serializeMediaAttachments:false attachments:_media];
|
|
return _mediaData;
|
|
}
|
|
|
|
- (BOOL)isEqualToConversation:(TGConversation *)other
|
|
{
|
|
if (_conversationId != other.conversationId || _outgoing != other.outgoing || _date != other.date || _fromUid != other.fromUid || ![_text isEqualToString:other.text] || _unreadCount != other.unreadCount || _serviceUnreadCount != other.serviceUnreadCount || _unread != other.unread || _isChat != other.isChat || _deliveryError != other.deliveryError || _deliveryState != other.deliveryState)
|
|
return false;
|
|
|
|
if (_media.count != other.media.count)
|
|
return false;
|
|
if (_media != nil && ![self.mediaData isEqualToData:other.mediaData])
|
|
return false;
|
|
|
|
if (_isChat)
|
|
{
|
|
if (![_chatTitle isEqualToString:other.chatTitle] || _chatVersion != other.chatVersion || _leftChat != other.leftChat || _kickedFromChat != other.kickedFromChat ||
|
|
(((_chatParticipants != nil) != (other.chatParticipants != nil)) || (_chatParticipants != nil && ![_chatParticipants.serializedData isEqualToData:other.chatParticipants.serializedData]))
|
|
)
|
|
return false;
|
|
if ((_chatPhotoSmall != nil) != (other.chatPhotoSmall != nil) || (_chatPhotoSmall != nil && ![_chatPhotoSmall isEqualToString:other.chatPhotoSmall]))
|
|
return false;
|
|
if ((_chatPhotoMedium != nil) != (other.chatPhotoMedium != nil) || (_chatPhotoMedium != nil && ![_chatPhotoMedium isEqualToString:other.chatPhotoMedium]))
|
|
return false;
|
|
if ((_chatPhotoBig != nil) != (other.chatPhotoBig != nil) || (_chatPhotoBig != nil && ![_chatPhotoBig isEqualToString:other.chatPhotoBig]))
|
|
return false;
|
|
}
|
|
|
|
if (_encryptedData != nil || other->_encryptedData != nil)
|
|
{
|
|
if ((_encryptedData != nil) != (other->_encryptedData != nil) || (_encryptedData != nil && ![_encryptedData isEqualToEncryptedData:other->_encryptedData]))
|
|
return false;
|
|
}
|
|
|
|
if (_flags != other->_flags) {
|
|
return false;
|
|
}
|
|
|
|
if (!TGStringCompare(_restrictionReason, other->_restrictionReason)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
- (BOOL)isEqualToConversationIgnoringMessage:(TGConversation *)other
|
|
{
|
|
if (_conversationId != other.conversationId || _isChat != other.isChat)
|
|
return false;
|
|
|
|
if (_isChat)
|
|
{
|
|
if (![_chatTitle isEqualToString:other.chatTitle] || _chatVersion != other.chatVersion || _leftChat != other.leftChat || _kickedFromChat != other.kickedFromChat ||
|
|
(((_chatParticipants != nil) != (other.chatParticipants != nil)) || (_chatParticipants != nil && ![_chatParticipants.serializedData isEqualToData:other.chatParticipants.serializedData]))
|
|
)
|
|
return false;
|
|
if ((_chatPhotoSmall != nil) != (other.chatPhotoSmall != nil) || (_chatPhotoSmall != nil && ![_chatPhotoSmall isEqualToString:other.chatPhotoSmall]))
|
|
return false;
|
|
if ((_chatPhotoMedium != nil) != (other.chatPhotoMedium != nil) || (_chatPhotoMedium != nil && ![_chatPhotoMedium isEqualToString:other.chatPhotoMedium]))
|
|
return false;
|
|
if ((_chatPhotoBig != nil) != (other.chatPhotoBig != nil) || (_chatPhotoBig != nil && ![_chatPhotoBig isEqualToString:other.chatPhotoBig]))
|
|
return false;
|
|
}
|
|
|
|
if (_flags != other->_flags) {
|
|
return false;
|
|
}
|
|
|
|
if (!TGStringCompare(_restrictionReason, other->_restrictionReason)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
- (NSData *)serializeChatPhoto
|
|
{
|
|
NSMutableData *data = [[NSMutableData alloc] init];
|
|
|
|
int32_t magic = 0x7acde441;
|
|
[data appendBytes:&magic length:4];
|
|
int32_t version = 4;
|
|
[data appendBytes:&version length:4];
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
NSString *value = nil;
|
|
if (i == 0)
|
|
value = _chatPhotoSmall;
|
|
else if (i == 1)
|
|
value = _chatPhotoMedium;
|
|
else if (i == 2)
|
|
value = _chatPhotoBig;
|
|
|
|
NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding];
|
|
int length = (int)valueData.length;
|
|
[data appendBytes:&length length:4];
|
|
if (valueData != nil)
|
|
[data appendData:valueData];
|
|
}
|
|
|
|
int8_t hasEncryptedData = _encryptedData != nil ? 1 : 0;
|
|
[data appendBytes:&hasEncryptedData length:1];
|
|
if (_encryptedData != nil)
|
|
[_encryptedData serialize:data];
|
|
|
|
[data appendBytes:&_flags length:4];
|
|
|
|
[data appendBytes:&_migratedToChannelId length:4];
|
|
[data appendBytes:&_migratedToChannelAccessHash length:8];
|
|
|
|
return data;
|
|
}
|
|
|
|
- (void)deserializeChatPhoto:(NSData *)data
|
|
{
|
|
int ptr = 0;
|
|
|
|
int32_t version = 1;
|
|
if (data.length >= 4) {
|
|
int32_t magic = 0;
|
|
[data getBytes:&magic length:4];
|
|
if (magic == 0x7acde441) {
|
|
ptr += 4;
|
|
|
|
[data getBytes:&version range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
int length = 0;
|
|
[data getBytes:&length range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
|
|
uint8_t *valueBytes = malloc(length);
|
|
[data getBytes:valueBytes range:NSMakeRange(ptr, length)];
|
|
ptr += length;
|
|
NSString *value = [[NSString alloc] initWithBytesNoCopy:valueBytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true];
|
|
|
|
if (i == 0)
|
|
_chatPhotoSmall = value;
|
|
else if (i == 1)
|
|
_chatPhotoMedium = value;
|
|
else if (i == 2)
|
|
_chatPhotoBig = value;
|
|
}
|
|
|
|
if (version == 1) {
|
|
if (ptr + 4 <= (int)data.length) {
|
|
_encryptedData = [TGEncryptedConversationData deserialize:data ptr:&ptr];
|
|
}
|
|
} else {
|
|
if (version >= 2) {
|
|
int8_t hasEncryptedData = 0;
|
|
[data getBytes:&hasEncryptedData range:NSMakeRange(ptr, 1)];
|
|
ptr += 1;
|
|
|
|
if (hasEncryptedData) {
|
|
_encryptedData = [TGEncryptedConversationData deserialize:data ptr:&ptr];
|
|
}
|
|
|
|
[data getBytes:&_flags range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
|
|
if (version >= 4) {
|
|
[data getBytes:&_migratedToChannelId range:NSMakeRange(ptr, 4)];
|
|
ptr += 4;
|
|
[data getBytes:&_migratedToChannelAccessHash range:NSMakeRange(ptr, 8)];
|
|
ptr += 8;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (bool)isEncrypted
|
|
{
|
|
return _encryptedData != nil;
|
|
}
|
|
|
|
- (void)mergeChannel:(TGConversation *)channel {
|
|
_chatTitle = channel.chatTitle;
|
|
_chatVersion = channel.chatVersion;
|
|
_chatPhotoBig = channel.chatPhotoBig;
|
|
_chatPhotoMedium = channel.chatPhotoMedium;
|
|
_chatPhotoSmall = channel.chatPhotoSmall;
|
|
_accessHash = channel.accessHash;
|
|
_username = channel.username;
|
|
_chatIsAdmin = channel.chatIsAdmin;
|
|
self.channelRole = channel.channelRole;
|
|
self.hasExplicitContent = channel.hasExplicitContent;
|
|
self.everybodyCanAddMembers = channel.everybodyCanAddMembers;
|
|
self.signaturesEnabled = channel.signaturesEnabled;
|
|
self.restrictionReason = channel.restrictionReason;
|
|
_channelIsReadOnly = channel.channelIsReadOnly;
|
|
_leftChat = channel.leftChat;
|
|
_kickedFromChat = channel.kickedFromChat;
|
|
self.kind = channel.leftChat || channel.kickedFromChat ? TGConversationKindTemporaryChannel : TGConversationKindPersistentChannel;
|
|
self.isVerified = channel.isVerified;
|
|
}
|
|
|
|
- (bool)currentUserCanSendMessages {
|
|
if (self.isChannelGroup) {
|
|
return true;
|
|
}
|
|
|
|
return (_channelRole == TGChannelRoleCreator || _channelRole == TGChannelRolePublisher || !_channelIsReadOnly) && !_leftChat && !_kickedFromChat;
|
|
}
|
|
|
|
+ (NSString *)chatTitleForDecoder:(PSKeyValueCoder *)coder {
|
|
return [coder decodeStringForCKey:"ct"];
|
|
}
|
|
|
|
- (bool)postAsChannel {
|
|
return _flags & TGConversationFlagPostAsChannel;
|
|
}
|
|
|
|
- (void)setPostAsChannel:(bool)postAsChannel {
|
|
if (postAsChannel) {
|
|
_flags |= TGConversationFlagPostAsChannel;
|
|
} else {
|
|
_flags &= ~TGConversationFlagPostAsChannel;
|
|
}
|
|
}
|
|
|
|
- (bool)displayExpanded {
|
|
return _flags & TGConversationFlagDisplayExpanded;
|
|
}
|
|
|
|
- (void)setDisplayExpanded:(bool)displayExpanded {
|
|
if (displayExpanded) {
|
|
_flags |= TGConversationFlagDisplayExpanded;
|
|
} else {
|
|
_flags &= ~TGConversationFlagDisplayExpanded;
|
|
}
|
|
}
|
|
|
|
- (bool)isVerified {
|
|
return _flags & TGConversationFlagVerified;
|
|
}
|
|
|
|
- (void)setIsVerified:(bool)isVerified {
|
|
if (isVerified) {
|
|
_flags |= TGConversationFlagVerified;
|
|
} else {
|
|
_flags &= ~TGConversationFlagVerified;
|
|
}
|
|
}
|
|
|
|
- (bool)hasExplicitContent {
|
|
return _flags & TGConversationFlagHasExplicitContent;
|
|
}
|
|
|
|
- (void)setHasExplicitContent:(bool)hasExplicitContent {
|
|
if (hasExplicitContent) {
|
|
_flags |= TGConversationFlagHasExplicitContent;
|
|
} else {
|
|
_flags &= ~TGConversationFlagHasExplicitContent;
|
|
}
|
|
}
|
|
|
|
- (bool)hasAdmins {
|
|
return _flags & TGConversationFlagHasAdmins;
|
|
}
|
|
|
|
- (void)setHasAdmins:(bool)hasAdmins {
|
|
if (hasAdmins) {
|
|
_flags |= TGConversationFlagHasAdmins;
|
|
} else {
|
|
_flags &= ~TGConversationFlagHasAdmins;
|
|
}
|
|
}
|
|
|
|
- (bool)isAdmin {
|
|
return _flags & TGConversationFlagIsAdmin;
|
|
}
|
|
|
|
- (void)setIsAdmin:(bool)isAdmin {
|
|
if (isAdmin) {
|
|
_flags |= TGConversationFlagIsAdmin;
|
|
} else {
|
|
_flags &= ~TGConversationFlagIsAdmin;
|
|
}
|
|
}
|
|
|
|
- (bool)isCreator {
|
|
return _flags & TGConversationFlagIsCreator;
|
|
}
|
|
|
|
- (void)setIsCreator:(bool)isCreator {
|
|
if (isCreator) {
|
|
_flags |= TGConversationFlagIsCreator;
|
|
} else {
|
|
_flags &= ~TGConversationFlagIsCreator;
|
|
}
|
|
}
|
|
|
|
- (bool)isChannelGroup {
|
|
return _flags & TGConversationFlagIsChannelGroup;
|
|
}
|
|
|
|
- (void)setIsChannelGroup:(bool)isChannelGroup {
|
|
if (isChannelGroup) {
|
|
_flags |= TGConversationFlagIsChannelGroup;
|
|
} else {
|
|
_flags &= ~TGConversationFlagIsChannelGroup;
|
|
}
|
|
}
|
|
|
|
- (bool)everybodyCanAddMembers {
|
|
return _flags & TGConversationFlagEverybodyCanAddMembers;
|
|
}
|
|
|
|
- (void)setEverybodyCanAddMembers:(bool)everybodyCanAddMembers {
|
|
if (everybodyCanAddMembers) {
|
|
_flags |= TGConversationFlagEverybodyCanAddMembers;
|
|
} else {
|
|
_flags &= ~TGConversationFlagEverybodyCanAddMembers;
|
|
}
|
|
}
|
|
|
|
- (bool)signaturesEnabled {
|
|
return _flags & TGConversationFlagSignaturesEnabled;
|
|
}
|
|
|
|
- (void)setSignaturesEnabled:(bool)signaturesEnabled {
|
|
if (signaturesEnabled) {
|
|
_flags |= TGConversationFlagSignaturesEnabled;
|
|
} else {
|
|
_flags &= ~TGConversationFlagSignaturesEnabled;
|
|
}
|
|
}
|
|
|
|
- (bool)isDeactivated {
|
|
return _flags & TGConversationFlagIsDeactivated;
|
|
}
|
|
|
|
- (void)setIsDeactivated:(bool)isDeactivated {
|
|
if (isDeactivated) {
|
|
_flags |= TGConversationFlagIsDeactivated;
|
|
} else {
|
|
_flags &= ~TGConversationFlagIsDeactivated;
|
|
}
|
|
}
|
|
|
|
@end
|