GroupMessageProcessor.m 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2015-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. #import "GroupMessageProcessor.h"
  21. #import "EntityManager.h"
  22. #import "MessageSender.h"
  23. #import "GroupProxy.h"
  24. #import "GroupCreateMessage.h"
  25. #import "GroupLeaveMessage.h"
  26. #import "GroupRequestSyncMessage.h"
  27. #import "GroupPhotoSender.h"
  28. #import "MyIdentityStore.h"
  29. #import "ContactStore.h"
  30. #ifdef DEBUG
  31. static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
  32. #else
  33. static const DDLogLevel ddLogLevel = DDLogLevelWarning;
  34. #endif
  35. @interface GroupMessageProcessor ()
  36. @property AbstractGroupMessage *message;
  37. @property GroupProxy *group;
  38. @property EntityManager *entityManager;
  39. @end
  40. @implementation GroupMessageProcessor
  41. + (instancetype)groupMessageProcessorForMessage:(AbstractGroupMessage *)message {
  42. return [[GroupMessageProcessor alloc] initWithMessage:message];
  43. }
  44. - (instancetype)initWithMessage:(AbstractGroupMessage *)message
  45. {
  46. self = [super init];
  47. if (self) {
  48. self.entityManager = [[EntityManager alloc] init];
  49. self.message = message;
  50. self.group = [self groupProxyForMessage:message];
  51. self.isNewGroup = NO;
  52. }
  53. return self;
  54. }
  55. - (void)handleMessageOnCompletion:(void (^)(BOOL))onCompletion onError:(void(^)(NSError *error))onError {
  56. if ([_message isKindOfClass:[GroupCreateMessage class]]) {
  57. [self processIncomingGroupCreateMessage:(GroupCreateMessage *)_message onCompletion:^{
  58. onCompletion(YES);
  59. } onError:^(NSError *error) {
  60. onError(error);
  61. }];
  62. return;
  63. }
  64. if (_group == nil) {
  65. /* Group not found. This can happen e.g. if the user reinstalls the app, so the app won't
  66. know anything about the group. */
  67. if ([_message.groupCreator isEqualToString:[MyIdentityStore sharedMyIdentityStore].identity]) {
  68. /* Sending a sync request to ourselves won't do any good. We don't know the member list of this
  69. group, so all we can do is send a leave message to the sender of this message */
  70. DDLogWarn(@"Group creator is ourselves, but group is unknown. Sending leave message to %@.", _message.fromIdentity);
  71. [MessageSender sendGroupLeaveMessageForCreator:_message.groupCreator groupId:_message.groupId toIdentity:_message.fromIdentity];
  72. } else if ([_message isKindOfClass:[GroupRequestSyncMessage class]] == NO) {
  73. // do that only if it's not from notification extension file
  74. [GroupProxy sendSyncRequestWithGroupId:_message.groupId creator:_message.groupCreator];
  75. _addToPendingMessages = YES;
  76. }
  77. onCompletion(YES);
  78. return;
  79. } else if (_group.didLeaveGroup) {
  80. [_group resendLeaveMessageTo: _message.fromIdentity];
  81. onCompletion(YES);
  82. return;
  83. } else if (_group.didRequestSync) {
  84. [_group resendSyncRequest];
  85. _addToPendingMessages = YES;
  86. onCompletion(YES);
  87. return;
  88. }
  89. if ([_group isGroupMember:_message.fromIdentity] == NO) {
  90. if (_group.isOwnGroup) {
  91. DDLogInfo(@"%@ is not member of group %@, resend group info", _message.fromIdentity, _message.groupId);
  92. [_group syncGroupInfoToIdentity:_message.fromIdentity];
  93. } else {
  94. if ([_message isKindOfClass:[GroupRequestSyncMessage class]] == NO) {
  95. // do that only if it's not from notification extension file
  96. [GroupProxy sendSyncRequestWithGroupId:_message.groupId creator:_message.groupCreator];
  97. _addToPendingMessages = YES;
  98. }
  99. DDLogInfo(@"%@ is not member of group %@, add to pending messages and request group sync", _message.fromIdentity, _message.groupId);
  100. }
  101. onCompletion(YES);
  102. return;
  103. }
  104. if ([_message isKindOfClass:[GroupLeaveMessage class]]) {
  105. [_group remoteGroupMemberLeft:_message.fromIdentity remoteSentDate:_message.date];
  106. onCompletion(YES);
  107. } else if ([_message isKindOfClass:[GroupRequestSyncMessage class]]) {
  108. [_group syncGroupInfoToIdentity:_message.fromIdentity];
  109. onCompletion(YES);
  110. } else {
  111. onCompletion(NO);
  112. }
  113. }
  114. - (Conversation *)conversation {
  115. return [_entityManager.entityFetcher conversationForGroupMessage:_message];
  116. }
  117. - (void)processIncomingGroupCreateMessage:(GroupCreateMessage *)msg onCompletion:(void(^)(void))onCompletion onError:(void(^)(NSError *error))onError {
  118. // Record a pseudo sync request so we won't trigger another one if we process
  119. // messages in this new group while we are still processing the group create
  120. [GroupProxy recordSyncRequestWithGroupId:msg.groupId creator:msg.groupCreator];
  121. // Ensure we have the public keys etc. of all new group members available before we start.
  122. // This avoids lots of individual identity fetch requests to the server.
  123. [[ContactStore sharedContactStore] prefetchIdentityInfo:[NSMutableSet setWithArray:msg.groupMembers] onCompletion:^{
  124. Conversation *conversation = [_entityManager.entityFetcher conversationForGroupMessage:msg];
  125. if (conversation) {
  126. DDLogVerbose(@"Group create: group already in DB - ID %@ from %@ already in database", msg.groupId, msg.fromIdentity);
  127. _group = [GroupProxy groupProxyForConversation:conversation];
  128. _isNewGroup = NO;
  129. NSMutableSet *members = [NSMutableSet setWithArray:msg.groupMembers];
  130. [members addObject:msg.fromIdentity];
  131. [self updateMembers:members remoteSentDate:msg.date];
  132. /* update GroupMyIdentity in conversation to send messages if we were (re-)added to existing group */
  133. [_group updateGroupMyIdentity:[MyIdentityStore sharedMyIdentityStore].identity forConversation:conversation];
  134. [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationUpdatedGroup object:self userInfo:@{@"groupId": _group.groupId, @"creatorString": _group.creatorString}];
  135. if ([_delegate respondsToSelector:@selector(startProcessPendingGroupMessages)]) {
  136. [_delegate startProcessPendingGroupMessages];
  137. }
  138. } else {
  139. DDLogVerbose(@"Group create: new group - creator %@, group ID %@, members %@", msg.fromIdentity, msg.groupId, msg.groupMembers);
  140. if (msg.groupMembers.count == 1) {
  141. NSString *memberIdentity = msg.groupMembers.firstObject;
  142. if ([memberIdentity isEqualToString:msg.groupCreator]) {
  143. onCompletion();
  144. return;
  145. }
  146. }
  147. _group = [self createNewGroupFromMessage:msg];
  148. _isNewGroup = YES;
  149. }
  150. onCompletion();
  151. } onError:^(NSError *error) {
  152. DDLogError(@"Cannot prefetch group members: %@", error);
  153. onError(error);
  154. }];
  155. }
  156. - (void)updateMembers:(NSSet *)memberIdentities remoteSentDate:(NSDate*)remoteSentDate {
  157. for (NSString *groupMemberId in _group.memberIdsIncludingSelf) {
  158. if ([memberIdentities containsObject:groupMemberId] == NO) {
  159. [_group remoteRemoveGroupMember:groupMemberId remoteSentDate:remoteSentDate];
  160. }
  161. }
  162. for (NSString *memberIdentity in memberIdentities) {
  163. if ([_group isGroupMember:memberIdentity] == NO) {
  164. [_group remoteAddGroupMember:memberIdentity notify:YES remoteSentDate:remoteSentDate];
  165. } else {
  166. if ([memberIdentity isEqualToString:[MyIdentityStore sharedMyIdentityStore].identity] && [_group didLeaveGroup] == true) {
  167. [_group remoteAddGroupMember:memberIdentity notify:YES remoteSentDate:remoteSentDate];
  168. }
  169. }
  170. }
  171. }
  172. - (GroupProxy *)groupProxyForMessage:(AbstractGroupMessage *)message {
  173. Conversation *conversation = [_entityManager.entityFetcher conversationForGroupMessage:message];
  174. return [GroupProxy groupProxyForConversation:conversation];
  175. }
  176. - (GroupProxy *)createNewGroupFromMessage:(GroupCreateMessage *)message {
  177. /* Find contact for group creator */
  178. Contact *creator = [_entityManager.entityFetcher contactForId:message.fromIdentity];
  179. if (creator == nil) {
  180. /* This should never happen, as without an entry in the contacts database, we wouldn't have
  181. been able to decrypt this message in the first place (no sender public key) */
  182. DDLogWarn(@"Identity %@ not in local contacts database - cannot process message", message.fromIdentity);
  183. return nil;
  184. }
  185. /* create new group */
  186. GroupProxy *group = [GroupProxy newGroupWithId:message.groupId creator:creator];
  187. [group remoteAddGroupMember:creator.identity notify:NO remoteSentDate:message.date];
  188. /* fetch all group members */
  189. for (NSString *memberIdentity in message.groupMembers) {
  190. [group remoteAddGroupMember:memberIdentity notify:NO remoteSentDate:message.date];
  191. }
  192. [_entityManager performSyncBlockAndSafe:nil];
  193. return group;
  194. }
  195. @end