Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
Path: blob/main/SignalServiceKit/Groups/TSGroupModel.m
1 views
1
//
2
// Copyright 2017 Signal Messenger, LLC
3
// SPDX-License-Identifier: AGPL-3.0-only
4
//
5
6
#import "TSGroupModel.h"
7
#import <SignalServiceKit/SignalServiceKit-Swift.h>
8
9
NS_ASSUME_NONNULL_BEGIN
10
11
const uint64_t kMaxEncryptedAvatarSize = 3 * 1024 * 1024;
12
const uint64_t kMaxAvatarSize = (kMaxEncryptedAvatarSize
13
/* The length of the padding. See GroupSecretParams:encrypt_blob_with_padding in LibSignal. */
14
- sizeof(uint32_t)
15
/* Overhead from GroupAttributeBlob (protobuf) via GroupV2Params.encryptGroupAvatar(_:). */
16
/* One byte for "2:LEN" & 4 bytes to represent more than 2 MiB of data (2^21). */
17
- 1
18
- 4
19
/* The padding bytes themselves. See ClientZkGroupCipher.encryptBlob(randomness:plaintext:). */
20
- 0
21
/* Encryption tag & nonce. See GroupSecretParams:encrypt_blob in LibSignal. */
22
- 16
23
- 12
24
/* Reserved byte. See GroupSecretParams:encrypt_blob in LibSignal. */
25
- 1);
26
const NSUInteger kGroupIdLengthV1 = 16;
27
const NSUInteger kGroupIdLengthV2 = 32;
28
29
NSUInteger const TSGroupModelSchemaVersion = 2;
30
31
@interface TSGroupModel ()
32
33
@property (nonatomic, readonly) NSUInteger groupModelSchemaVersion;
34
35
@end
36
37
#pragma mark -
38
39
@implementation TSGroupModel
40
41
@synthesize groupName = _groupName;
42
43
#if TARGET_OS_IOS
44
45
- (instancetype)initWithGroupId:(NSData *)groupId
46
name:(nullable NSString *)name
47
avatarData:(nullable NSData *)avatarData
48
members:(NSArray<SignalServiceAddress *> *)members
49
addedByAddress:(nullable SignalServiceAddress *)addedByAddress
50
{
51
self = [super init];
52
if (!self) {
53
return self;
54
}
55
56
_groupId = groupId;
57
_groupName = name;
58
_groupMembers = members;
59
_addedByAddress = addedByAddress;
60
_groupModelSchemaVersion = TSGroupModelSchemaVersion;
61
62
if (avatarData) {
63
NSError *error;
64
[self persistAvatarData:avatarData error:&error];
65
if (error) {
66
OWSFailDebug(@"Failed to persist group avatar data %@", error);
67
}
68
}
69
70
OWSAssertDebug([GroupManager isValidGroupId:groupId groupsVersion:self.groupsVersion]);
71
72
return self;
73
}
74
75
+ (BOOL)supportsSecureCoding
76
{
77
return YES;
78
}
79
80
- (void)encodeWithCoder:(NSCoder *)coder
81
{
82
SignalServiceAddress *addedByAddress = self.addedByAddress;
83
if (addedByAddress != nil) {
84
[coder encodeObject:addedByAddress forKey:@"addedByAddress"];
85
}
86
NSString *avatarHash = self.avatarHash;
87
if (avatarHash != nil) {
88
[coder encodeObject:avatarHash forKey:@"avatarHash"];
89
}
90
NSData *groupId = self.groupId;
91
if (groupId != nil) {
92
[coder encodeObject:groupId forKey:@"groupId"];
93
}
94
if (![self isKindOfClass:[TSGroupModelV2 class]]) {
95
NSArray *groupMembers = self.groupMembers;
96
if (groupMembers != nil) {
97
[coder encodeObject:groupMembers forKey:@"groupMembers"];
98
}
99
}
100
[coder encodeObject:[self valueForKey:@"groupModelSchemaVersion"] forKey:@"groupModelSchemaVersion"];
101
NSString *groupName = self.groupName;
102
if (groupName != nil) {
103
[coder encodeObject:groupName forKey:@"groupName"];
104
}
105
NSData *legacyAvatarData = self.legacyAvatarData;
106
if (legacyAvatarData != nil) {
107
[coder encodeObject:legacyAvatarData forKey:@"legacyAvatarData"];
108
}
109
}
110
111
- (nullable instancetype)initWithCoder:(NSCoder *)coder
112
{
113
self = [super init];
114
if (!self) {
115
return self;
116
}
117
self->_addedByAddress = [coder decodeObjectOfClass:[SignalServiceAddress class] forKey:@"addedByAddress"];
118
self->_avatarHash = [coder decodeObjectOfClass:[NSString class] forKey:@"avatarHash"];
119
self->_groupId = [coder decodeObjectOfClass:[NSData class] forKey:@"groupId"];
120
self->_groupMembers =
121
[coder decodeObjectOfClasses:[NSSet setWithArray:@[ [NSArray class], [SignalServiceAddress class] ]]
122
forKey:@"groupMembers"];
123
self->_groupModelSchemaVersion =
124
[(NSNumber *)[coder decodeObjectOfClass:[NSNumber class]
125
forKey:@"groupModelSchemaVersion"] unsignedIntegerValue];
126
self->_groupName = [coder decodeObjectOfClass:[NSString class] forKey:@"groupName"];
127
self->_legacyAvatarData = [coder decodeObjectOfClass:[NSData class] forKey:@"legacyAvatarData"];
128
129
OWSAssertDebug([GroupManager isValidGroupId:self.groupId groupsVersion:self.groupsVersion]);
130
131
if (_groupModelSchemaVersion < 1) {
132
NSArray<NSString *> *_Nullable memberE164s =
133
[coder decodeObjectOfClasses:[NSSet setWithArray:@[ [NSArray class], [NSString class] ]]
134
forKey:@"groupMemberIds"];
135
if (memberE164s) {
136
NSMutableArray<SignalServiceAddress *> *memberAddresses = [NSMutableArray new];
137
for (NSString *phoneNumber in memberE164s) {
138
[memberAddresses addObject:[SignalServiceAddress legacyAddressWithServiceIdString:nil
139
phoneNumber:phoneNumber]];
140
}
141
_groupMembers = [memberAddresses copy];
142
} else {
143
_groupMembers = @[];
144
}
145
}
146
147
if (_groupModelSchemaVersion < 2) {
148
_legacyAvatarData = [coder decodeObjectOfClass:[NSData class] forKey:@"groupAvatarData"];
149
}
150
151
_groupModelSchemaVersion = TSGroupModelSchemaVersion;
152
153
return self;
154
}
155
156
- (NSUInteger)hash
157
{
158
NSUInteger result = 0;
159
result ^= self.addedByAddress.hash;
160
result ^= self.avatarHash.hash;
161
result ^= self.groupId.hash;
162
result ^= self.groupMembers.hash;
163
result ^= self.groupModelSchemaVersion;
164
result ^= self.groupName.hash;
165
result ^= self.legacyAvatarData.hash;
166
return result;
167
}
168
169
- (BOOL)isEqual:(id)other
170
{
171
if (![other isMemberOfClass:self.class]) {
172
return NO;
173
}
174
TSGroupModel *typedOther = (TSGroupModel *)other;
175
if (![NSObject isObject:self.addedByAddress equalToObject:typedOther.addedByAddress]) {
176
return NO;
177
}
178
if (![NSObject isObject:self.avatarHash equalToObject:typedOther.avatarHash]) {
179
return NO;
180
}
181
if (![NSObject isObject:self.groupId equalToObject:typedOther.groupId]) {
182
return NO;
183
}
184
if (![NSObject isObject:self.groupMembers equalToObject:typedOther.groupMembers]) {
185
return NO;
186
}
187
if (self.groupModelSchemaVersion != typedOther.groupModelSchemaVersion) {
188
return NO;
189
}
190
if (![NSObject isObject:self.groupName equalToObject:typedOther.groupName]) {
191
return NO;
192
}
193
if (![NSObject isObject:self.legacyAvatarData equalToObject:typedOther.legacyAvatarData]) {
194
return NO;
195
}
196
return YES;
197
}
198
199
- (id)copyWithZone:(nullable NSZone *)zone
200
{
201
TSGroupModel *result = [[[self class] allocWithZone:zone] init];
202
result->_addedByAddress = self.addedByAddress;
203
result->_avatarHash = self.avatarHash;
204
result->_groupId = self.groupId;
205
result->_groupMembers = self.groupMembers;
206
result->_groupModelSchemaVersion = self.groupModelSchemaVersion;
207
result->_groupName = self.groupName;
208
result->_legacyAvatarData = self.legacyAvatarData;
209
return result;
210
}
211
212
- (GroupsVersion)groupsVersion
213
{
214
return GroupsVersionV1;
215
}
216
217
- (GroupMembership *)groupMembership
218
{
219
return [[GroupMembership alloc] initWithV1Members:self.groupMembers];
220
}
221
222
#endif
223
224
- (nullable NSString *)groupName
225
{
226
return _groupName.filterStringForDisplay;
227
}
228
229
- (NSString *)groupNameOrDefault
230
{
231
NSString *_Nullable groupName = [self.groupName filterStringForDisplay];
232
return groupName.length > 0 ? groupName : TSGroupThread.defaultGroupName;
233
}
234
235
@end
236
237
NS_ASSUME_NONNULL_END
238
239