Path: blob/main/SignalServiceKit/Groups/TSGroupModel.m
1 views
//1// Copyright 2017 Signal Messenger, LLC2// SPDX-License-Identifier: AGPL-3.0-only3//45#import "TSGroupModel.h"6#import <SignalServiceKit/SignalServiceKit-Swift.h>78NS_ASSUME_NONNULL_BEGIN910const uint64_t kMaxEncryptedAvatarSize = 3 * 1024 * 1024;11const uint64_t kMaxAvatarSize = (kMaxEncryptedAvatarSize12/* The length of the padding. See GroupSecretParams:encrypt_blob_with_padding in LibSignal. */13- sizeof(uint32_t)14/* Overhead from GroupAttributeBlob (protobuf) via GroupV2Params.encryptGroupAvatar(_:). */15/* One byte for "2:LEN" & 4 bytes to represent more than 2 MiB of data (2^21). */16- 117- 418/* The padding bytes themselves. See ClientZkGroupCipher.encryptBlob(randomness:plaintext:). */19- 020/* Encryption tag & nonce. See GroupSecretParams:encrypt_blob in LibSignal. */21- 1622- 1223/* Reserved byte. See GroupSecretParams:encrypt_blob in LibSignal. */24- 1);25const NSUInteger kGroupIdLengthV1 = 16;26const NSUInteger kGroupIdLengthV2 = 32;2728NSUInteger const TSGroupModelSchemaVersion = 2;2930@interface TSGroupModel ()3132@property (nonatomic, readonly) NSUInteger groupModelSchemaVersion;3334@end3536#pragma mark -3738@implementation TSGroupModel3940@synthesize groupName = _groupName;4142#if TARGET_OS_IOS4344- (instancetype)initWithGroupId:(NSData *)groupId45name:(nullable NSString *)name46avatarData:(nullable NSData *)avatarData47members:(NSArray<SignalServiceAddress *> *)members48addedByAddress:(nullable SignalServiceAddress *)addedByAddress49{50self = [super init];51if (!self) {52return self;53}5455_groupId = groupId;56_groupName = name;57_groupMembers = members;58_addedByAddress = addedByAddress;59_groupModelSchemaVersion = TSGroupModelSchemaVersion;6061if (avatarData) {62NSError *error;63[self persistAvatarData:avatarData error:&error];64if (error) {65OWSFailDebug(@"Failed to persist group avatar data %@", error);66}67}6869OWSAssertDebug([GroupManager isValidGroupId:groupId groupsVersion:self.groupsVersion]);7071return self;72}7374+ (BOOL)supportsSecureCoding75{76return YES;77}7879- (void)encodeWithCoder:(NSCoder *)coder80{81SignalServiceAddress *addedByAddress = self.addedByAddress;82if (addedByAddress != nil) {83[coder encodeObject:addedByAddress forKey:@"addedByAddress"];84}85NSString *avatarHash = self.avatarHash;86if (avatarHash != nil) {87[coder encodeObject:avatarHash forKey:@"avatarHash"];88}89NSData *groupId = self.groupId;90if (groupId != nil) {91[coder encodeObject:groupId forKey:@"groupId"];92}93if (![self isKindOfClass:[TSGroupModelV2 class]]) {94NSArray *groupMembers = self.groupMembers;95if (groupMembers != nil) {96[coder encodeObject:groupMembers forKey:@"groupMembers"];97}98}99[coder encodeObject:[self valueForKey:@"groupModelSchemaVersion"] forKey:@"groupModelSchemaVersion"];100NSString *groupName = self.groupName;101if (groupName != nil) {102[coder encodeObject:groupName forKey:@"groupName"];103}104NSData *legacyAvatarData = self.legacyAvatarData;105if (legacyAvatarData != nil) {106[coder encodeObject:legacyAvatarData forKey:@"legacyAvatarData"];107}108}109110- (nullable instancetype)initWithCoder:(NSCoder *)coder111{112self = [super init];113if (!self) {114return self;115}116self->_addedByAddress = [coder decodeObjectOfClass:[SignalServiceAddress class] forKey:@"addedByAddress"];117self->_avatarHash = [coder decodeObjectOfClass:[NSString class] forKey:@"avatarHash"];118self->_groupId = [coder decodeObjectOfClass:[NSData class] forKey:@"groupId"];119self->_groupMembers =120[coder decodeObjectOfClasses:[NSSet setWithArray:@[ [NSArray class], [SignalServiceAddress class] ]]121forKey:@"groupMembers"];122self->_groupModelSchemaVersion =123[(NSNumber *)[coder decodeObjectOfClass:[NSNumber class]124forKey:@"groupModelSchemaVersion"] unsignedIntegerValue];125self->_groupName = [coder decodeObjectOfClass:[NSString class] forKey:@"groupName"];126self->_legacyAvatarData = [coder decodeObjectOfClass:[NSData class] forKey:@"legacyAvatarData"];127128OWSAssertDebug([GroupManager isValidGroupId:self.groupId groupsVersion:self.groupsVersion]);129130if (_groupModelSchemaVersion < 1) {131NSArray<NSString *> *_Nullable memberE164s =132[coder decodeObjectOfClasses:[NSSet setWithArray:@[ [NSArray class], [NSString class] ]]133forKey:@"groupMemberIds"];134if (memberE164s) {135NSMutableArray<SignalServiceAddress *> *memberAddresses = [NSMutableArray new];136for (NSString *phoneNumber in memberE164s) {137[memberAddresses addObject:[SignalServiceAddress legacyAddressWithServiceIdString:nil138phoneNumber:phoneNumber]];139}140_groupMembers = [memberAddresses copy];141} else {142_groupMembers = @[];143}144}145146if (_groupModelSchemaVersion < 2) {147_legacyAvatarData = [coder decodeObjectOfClass:[NSData class] forKey:@"groupAvatarData"];148}149150_groupModelSchemaVersion = TSGroupModelSchemaVersion;151152return self;153}154155- (NSUInteger)hash156{157NSUInteger result = 0;158result ^= self.addedByAddress.hash;159result ^= self.avatarHash.hash;160result ^= self.groupId.hash;161result ^= self.groupMembers.hash;162result ^= self.groupModelSchemaVersion;163result ^= self.groupName.hash;164result ^= self.legacyAvatarData.hash;165return result;166}167168- (BOOL)isEqual:(id)other169{170if (![other isMemberOfClass:self.class]) {171return NO;172}173TSGroupModel *typedOther = (TSGroupModel *)other;174if (![NSObject isObject:self.addedByAddress equalToObject:typedOther.addedByAddress]) {175return NO;176}177if (![NSObject isObject:self.avatarHash equalToObject:typedOther.avatarHash]) {178return NO;179}180if (![NSObject isObject:self.groupId equalToObject:typedOther.groupId]) {181return NO;182}183if (![NSObject isObject:self.groupMembers equalToObject:typedOther.groupMembers]) {184return NO;185}186if (self.groupModelSchemaVersion != typedOther.groupModelSchemaVersion) {187return NO;188}189if (![NSObject isObject:self.groupName equalToObject:typedOther.groupName]) {190return NO;191}192if (![NSObject isObject:self.legacyAvatarData equalToObject:typedOther.legacyAvatarData]) {193return NO;194}195return YES;196}197198- (id)copyWithZone:(nullable NSZone *)zone199{200TSGroupModel *result = [[[self class] allocWithZone:zone] init];201result->_addedByAddress = self.addedByAddress;202result->_avatarHash = self.avatarHash;203result->_groupId = self.groupId;204result->_groupMembers = self.groupMembers;205result->_groupModelSchemaVersion = self.groupModelSchemaVersion;206result->_groupName = self.groupName;207result->_legacyAvatarData = self.legacyAvatarData;208return result;209}210211- (GroupsVersion)groupsVersion212{213return GroupsVersionV1;214}215216- (GroupMembership *)groupMembership217{218return [[GroupMembership alloc] initWithV1Members:self.groupMembers];219}220221#endif222223- (nullable NSString *)groupName224{225return _groupName.filterStringForDisplay;226}227228- (NSString *)groupNameOrDefault229{230NSString *_Nullable groupName = [self.groupName filterStringForDisplay];231return groupName.length > 0 ? groupName : TSGroupThread.defaultGroupName;232}233234@end235236NS_ASSUME_NONNULL_END237238239