GroupProxy.m 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  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 "GroupProxy.h"
  21. #import "MessageSender.h"
  22. #import "EntityManager.h"
  23. #import "Group.h"
  24. #import "ContactStore.h"
  25. #import "MyIdentityStore.h"
  26. #import "GroupPhotoSender.h"
  27. #import "BundleUtil.h"
  28. #import "UserSettings.h"
  29. #ifdef DEBUG
  30. static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
  31. #else
  32. static const DDLogLevel ddLogLevel = DDLogLevelWarning;
  33. #endif
  34. @interface GroupProxy ()
  35. @property (nonatomic) Conversation *conversation;
  36. @property (nonatomic) Group *group;
  37. @property LastGroupSyncRequest *lastSyncRequest;
  38. @end
  39. @implementation GroupProxy
  40. + (instancetype)groupProxyForMessage:(AbstractGroupMessage *)message {
  41. EntityManager *entityManager = [[EntityManager alloc] init];
  42. Conversation *conversation = [entityManager.entityFetcher conversationForGroupMessage:message];
  43. if (conversation) {
  44. // valid existing group
  45. return [GroupProxy groupProxyForConversation:conversation entityManager:entityManager];
  46. }
  47. Group *group = [entityManager.entityFetcher groupForGroupId:message.groupId groupCreator:message.groupCreator];
  48. if (group ) {
  49. return [GroupProxy groupProxyForGroup:group];
  50. }
  51. /* Check if we have already requested a sync for this group in the last 7 days */
  52. NSDate *date = [NSDate dateWithTimeIntervalSinceNow:-kGroupSyncRequestInterval];
  53. LastGroupSyncRequest *lastSyncRequest = [entityManager.entityFetcher lastGroupSyncRequestFor:message.groupId groupCreator:message.groupCreator sinceDate:date];
  54. if (lastSyncRequest) {
  55. // group info not available yet
  56. return [GroupProxy groupForLastGroupSyncRequest:lastSyncRequest];
  57. }
  58. return nil;
  59. }
  60. + (instancetype)groupProxyForGroup:(Group *)group {
  61. if (group == nil) {
  62. return nil;
  63. }
  64. return [[GroupProxy alloc] initWithGroup: group];
  65. }
  66. + (instancetype)groupForLastGroupSyncRequest:(LastGroupSyncRequest *)lastSyncRequest {
  67. if (lastSyncRequest == nil) {
  68. return nil;
  69. }
  70. return [[GroupProxy alloc] initWithLastGroupSyncRequest: lastSyncRequest];
  71. }
  72. + (instancetype)groupProxyForConversation:(Conversation *)conversation {
  73. return [GroupProxy groupProxyForConversation:conversation entityManager:[[EntityManager alloc] init]];
  74. }
  75. + (instancetype)groupProxyForConversation:(Conversation *)conversation entityManager:(EntityManager*)entityManager {
  76. if (conversation == nil || conversation.isGroup == NO) {
  77. DDLogError(@"Conversation is not a group");
  78. return nil;
  79. }
  80. return [[GroupProxy alloc] initWithConversation: conversation entityManager:entityManager];
  81. }
  82. + (instancetype)newGroupWithId:(NSData *)groupId creator:(Contact *)creator {
  83. EntityManager *entityManager = [[EntityManager alloc] init];
  84. Contact *creatorInOwnContext = (Contact *)[entityManager.entityFetcher getManagedObjectById:creator.objectID];
  85. Conversation *conversation = [entityManager.entityCreator conversation];
  86. conversation.contact = creatorInOwnContext;
  87. conversation.groupId = groupId;
  88. conversation.groupMyIdentity = [MyIdentityStore sharedMyIdentityStore].identity;
  89. return [[GroupProxy alloc] initWithConversation: conversation entityManager:entityManager];
  90. }
  91. - (instancetype)initWithConversation:(Conversation *)conversation entityManager:(EntityManager*)entityManager {
  92. self = [super init];
  93. if (self) {
  94. _conversation = conversation;
  95. _group = [entityManager.entityFetcher groupForConversation:_conversation];
  96. }
  97. return self;
  98. }
  99. - (instancetype)initWithGroup:(Group *)group {
  100. self = [super init];
  101. if (self) {
  102. _group = group;
  103. }
  104. return self;
  105. }
  106. - (instancetype)initWithLastGroupSyncRequest:(LastGroupSyncRequest *)lastSyncRequest {
  107. self = [super init];
  108. if (self) {
  109. _lastSyncRequest = lastSyncRequest;
  110. EntityManager *entityManager = [[EntityManager alloc] init];
  111. _group = [entityManager.entityFetcher groupForGroupId:lastSyncRequest.groupId groupCreator:lastSyncRequest.groupCreator];
  112. }
  113. return self;
  114. }
  115. - (NSData *)groupId {
  116. return _conversation.groupId;
  117. }
  118. - (NSString *)name {
  119. return [_conversation displayName];
  120. }
  121. - (NSSet *)members {
  122. return _conversation.members;
  123. }
  124. - (NSSet *)memberIdsIncludingSelf {
  125. NSMutableSet *currentGroupMemberIds = [NSMutableSet set];
  126. for (Contact *contact in self.members) {
  127. [currentGroupMemberIds addObject:contact.identity];
  128. }
  129. if (self.group.state != nil) {
  130. if (self.group.state.intValue == kGroupStateActive) {
  131. [currentGroupMemberIds addObject: [MyIdentityStore sharedMyIdentityStore].identity];
  132. }
  133. }
  134. return currentGroupMemberIds;
  135. }
  136. - (NSSet *)activeMembers {
  137. if (self.members == nil) {
  138. return [NSMutableSet set];
  139. }
  140. if (self.members.count == 0) {
  141. return [NSMutableSet set];
  142. }
  143. NSMutableSet *activeMembers = [NSMutableSet setWithCapacity:self.members.count];
  144. for (Contact *member in self.members) {
  145. if (member.state.intValue != kStateInvalid) {
  146. [activeMembers addObject:member];
  147. }
  148. }
  149. return activeMembers;
  150. }
  151. - (NSArray *)sortedActiveMembers {
  152. /* Extract members without first or last name, and put them at the end of the list
  153. (otherwise they would appear on top) */
  154. NSMutableArray *namedMembers = [NSMutableArray array];
  155. NSMutableArray *unnamedMembers = [NSMutableArray array];
  156. for (Contact *contact in self.members) {
  157. if (contact.firstName.length == 0 && contact.lastName.length == 0)
  158. [unnamedMembers addObject:contact];
  159. else
  160. [namedMembers addObject:contact];
  161. }
  162. NSArray *sortedNamedMembers;
  163. if ([UserSettings sharedUserSettings].sortOrderFirstName) {
  164. sortedNamedMembers = [namedMembers sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"firstName" ascending:YES],
  165. [NSSortDescriptor sortDescriptorWithKey:@"lastName" ascending:YES],
  166. [NSSortDescriptor sortDescriptorWithKey:@"identity" ascending:YES]]];
  167. } else {
  168. sortedNamedMembers = [namedMembers sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"lastName" ascending:YES],
  169. [NSSortDescriptor sortDescriptorWithKey:@"firstName" ascending:YES],
  170. [NSSortDescriptor sortDescriptorWithKey:@"identity" ascending:YES]]];
  171. }
  172. NSArray *sortedUnnamedMembers = [unnamedMembers sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"identity" ascending:YES]]];
  173. return [sortedNamedMembers arrayByAddingObjectsFromArray:sortedUnnamedMembers];
  174. }
  175. - (NSSet *)activeMemberIds {
  176. NSMutableSet *activeMemberIds = [NSMutableSet setWithCapacity:self.activeMembers.count];
  177. for (Contact *member in self.activeMembers) {
  178. [activeMemberIds addObject:member.identity];
  179. }
  180. return activeMemberIds;
  181. }
  182. - (void)setName:(NSString *)name remoteSentDate:(NSDate*)remoteSentDate {
  183. if ([_conversation.groupName isEqualToString:name]) {
  184. /* no change */
  185. return;
  186. }
  187. _conversation.groupName = name;
  188. NSData *arg = [name dataUsingEncoding:NSUTF8StringEncoding];
  189. [self postSystemMessageType:kSystemMessageRenameGroup withArg:arg remoteSentDate:remoteSentDate];
  190. }
  191. - (BOOL)didLeaveGroup {
  192. if (_group && _group.didLeave) {
  193. return YES;
  194. }
  195. return NO;
  196. }
  197. - (BOOL)didRequestSync {
  198. if (_lastSyncRequest) {
  199. return YES;
  200. }
  201. return NO;
  202. }
  203. - (BOOL)canSendInGroup {
  204. return _group.didLeave == NO && _group.didForcedLeave == NO;
  205. }
  206. - (Contact *)contactForMemberIdentity:(NSString *)identity {
  207. if ([self.creator.identity isEqualToString:identity]) {
  208. return self.creator;
  209. }
  210. for (Contact *member in self.members) {
  211. if ([member.identity isEqualToString:identity]) {
  212. return member;
  213. }
  214. }
  215. return nil;
  216. }
  217. - (BOOL)isOwnGroup {
  218. if (_conversation.groupMyIdentity != nil && ![_conversation.groupMyIdentity isEqualToString:[MyIdentityStore sharedMyIdentityStore].identity]) {
  219. return NO;
  220. }
  221. if (self.group.state.intValue != kGroupStateActive) {
  222. return NO;
  223. }
  224. return _conversation.contact == nil;
  225. }
  226. - (BOOL)isSelfMember {
  227. if ([self isOwnGroup] && self.group.state.intValue == kGroupStateActive) {
  228. return YES;
  229. }
  230. if ([_conversation.groupMyIdentity isEqualToString:[MyIdentityStore sharedMyIdentityStore].identity]) {
  231. if (self.group != nil) {
  232. if (self.group.state != nil) {
  233. if (self.group.state.intValue == kGroupStateActive) {
  234. return YES;
  235. }
  236. }
  237. }
  238. }
  239. return NO;
  240. }
  241. - (Conversation *)conversation {
  242. return _conversation;
  243. }
  244. - (Contact *)creator {
  245. return _conversation.contact;
  246. }
  247. - (NSString *)creatorString {
  248. NSString *creator;
  249. if ([self isOwnGroup]) {
  250. creator = [BundleUtil localizedStringForKey:@"me"];
  251. } else if (self.creator) {
  252. creator = self.creator.displayName;
  253. } else {
  254. creator = [BundleUtil localizedStringForKey:@"(unknown)"];
  255. }
  256. return [NSString stringWithFormat:@"%@: %@", [BundleUtil localizedStringForKey:@"creator"], creator];
  257. }
  258. - (NSString *)membersSummaryString {
  259. NSInteger count = [self memberIdsIncludingSelf].count;
  260. NSString *summary = [BundleUtil localizedStringForKey:@"%d members"];
  261. return [NSString stringWithFormat:summary, count];
  262. }
  263. - (BOOL)isGroupMember:(NSString *)contactIdentity {
  264. NSSet *allMembers = [self memberIdsIncludingSelf];
  265. return [allMembers containsObject:contactIdentity];
  266. }
  267. //Has to be called with entity manager which loaded _conversation -> refactoring: use one entity manager in whole class
  268. - (void)adminAddMembersFromBackup:(NSSet *)identities entityManager:(EntityManager*)entityManager {
  269. //adding all members to group, restore from safe backup
  270. if ([identities count] > 0) {
  271. NSMutableArray *identitiesUppercase = [[NSMutableArray alloc] initWithCapacity:[identities count]];
  272. for (NSString *identity in identities) {
  273. NSString *identityUppercase = identity.uppercaseString;
  274. [identitiesUppercase addObject:identityUppercase];
  275. // do not add it's me
  276. Contact *member = [entityManager.entityFetcher contactForId:identityUppercase];
  277. if (member != nil && member.identity != [[MyIdentityStore sharedMyIdentityStore] identity]) {
  278. [_conversation addMembersObject:member];
  279. }
  280. }
  281. if ([identitiesUppercase containsObject:[[MyIdentityStore sharedMyIdentityStore] identity]]) {
  282. [self setGroupState:kGroupStateActive];
  283. } else {
  284. [self setGroupState:kGroupStateForcedLeft];
  285. }
  286. } else {
  287. [self setGroupState:kGroupStateLeft];
  288. }
  289. }
  290. - (void)adminAddMember:(Contact *)contact {
  291. // do not add it's me
  292. if ([contact.identity isEqualToString:[MyIdentityStore sharedMyIdentityStore].identity]) {
  293. return;
  294. }
  295. [_conversation addMembersObject:contact];
  296. [self postSystemMessageForMember:contact type:kSystemMessageGroupMemberAdd remoteSentDate:nil];
  297. [self syncGroupInfoToContact:contact];
  298. [self sendGroupCreateToAllMembers];
  299. }
  300. - (void)adminRemoveMember:(Contact *)contact {
  301. if ([contact.identity isEqualToString:[MyIdentityStore sharedMyIdentityStore].identity]) {
  302. // it's me in member remove me, otherwise delete group
  303. if ([[_conversation members] containsObject:contact]) {
  304. [_conversation removeMembersObject:contact];
  305. } else {
  306. [self adminDeleteGroup];
  307. }
  308. return;
  309. }
  310. [_conversation removeMembersObject:contact];
  311. [self postSystemMessageForMember:contact type:kSystemMessageGroupMemberForcedLeave remoteSentDate:nil];
  312. // send to removed member
  313. [MessageSender sendGroupCreateMessageForGroup:self toMember:contact];
  314. [self sendGroupCreateToAllMembers];
  315. }
  316. - (void)groupLeavePostprocess:(Contact *)member {
  317. // delete member vote in all open ballots
  318. for (Ballot *ballot in _conversation.ballots) {
  319. if ([ballot isClosed] == NO) {
  320. for (BallotChoice *choice in ballot.choices) {
  321. [choice removeResultForContact: member.identity];
  322. }
  323. }
  324. }
  325. }
  326. - (void)resendLeaveMessageTo:(NSString *)identity {
  327. if (_group) {
  328. NSData *groupId = _group.groupId;
  329. NSString *groupCreator = _group.groupCreator;
  330. DDLogWarn(@"Group ID %@ (creator %@) has been deleted before. Sending leave message.", groupId, groupCreator);
  331. [MessageSender sendGroupLeaveMessageForCreator:groupCreator groupId:groupId toIdentity:identity];
  332. if (![identity isEqualToString:groupCreator]) {
  333. // also resend message to group creator to make sure
  334. [MessageSender sendGroupLeaveMessageForCreator:groupCreator groupId:groupId toIdentity:groupCreator];
  335. }
  336. }
  337. }
  338. - (void)leaveGroup {
  339. [MessageSender sendGroupLeaveMessageForConversation:_conversation];
  340. [self setGroupState:kGroupStateLeft];
  341. [self postSystemMessageType:kSystemMessageGroupSelfLeft withArg:nil remoteSentDate:[NSDate date]];
  342. [self updateGroupMyIdentity:[MyIdentityStore sharedMyIdentityStore].identity forConversation:_conversation];
  343. }
  344. + (void)sendSyncRequestWithGroupId:(NSData *)groupId creator:(NSString *)groupCreator {
  345. EntityManager *entityManager = [[EntityManager alloc] init];
  346. NSDate *date = [NSDate dateWithTimeIntervalSinceNow:-kGroupSyncRequestInterval];
  347. LastGroupSyncRequest *lastSyncRequest = [entityManager.entityFetcher lastGroupSyncRequestFor:groupId groupCreator:groupCreator sinceDate:date];
  348. if (lastSyncRequest) {
  349. DDLogInfo(@"Sync for Group ID %@ (creator %@) already requested.", groupId, groupCreator);
  350. } else {
  351. DDLogWarn(@"Group ID %@ (creator %@) not found. Requesting sync from creator.", groupId, groupCreator);
  352. [self sendGroupRequestSyncMessageForCreator:groupCreator groupId:groupId];
  353. [GroupProxy recordSyncRequestWithGroupId:groupId creator:groupCreator];
  354. }
  355. }
  356. + (void)recordSyncRequestWithGroupId:(NSData *)groupId creator:(NSString *)groupCreator {
  357. EntityManager *entityManager = [[EntityManager alloc] init];
  358. [entityManager performSyncBlockAndSafe:^{
  359. /* Record this sync request */
  360. LastGroupSyncRequest *lastSyncRequest = [entityManager.entityCreator lastGroupSyncRequest];
  361. lastSyncRequest.groupCreator = groupCreator;
  362. lastSyncRequest.groupId = groupId;
  363. lastSyncRequest.lastSyncRequest = [NSDate date];
  364. }];
  365. }
  366. + (void)sendGroupRequestSyncMessageForCreator:(NSString*)creator groupId:(NSData*)groupId {
  367. /* Fetch creator public key first if necessary */
  368. EntityManager *entityManager = [[EntityManager alloc] init];
  369. __block Contact *creatorContact = [entityManager.entityFetcher contactForId:creator];
  370. if (creatorContact == nil) {
  371. /* must fetch key */
  372. [[ContactStore sharedContactStore] fetchPublicKeyForIdentity:creator onCompletion:^(NSData *publicKey) {
  373. dispatch_async(dispatch_get_main_queue(), ^{
  374. creatorContact = [entityManager.entityFetcher contactForId:creator];
  375. if (creatorContact != nil) {
  376. [MessageSender sendGroupRequestSyncMessageForCreatorContact:creatorContact groupId:groupId];
  377. }
  378. });
  379. } onError:^(NSError *error) {
  380. DDLogWarn(@"Could not fetch public key for %@: %@", creator, error);
  381. }];
  382. } else {
  383. [MessageSender sendGroupRequestSyncMessageForCreatorContact:creatorContact groupId:groupId];
  384. }
  385. }
  386. - (void)resendSyncRequest {
  387. if (_lastSyncRequest) {
  388. [GroupProxy sendSyncRequestWithGroupId:_lastSyncRequest.groupId creator:_lastSyncRequest.groupCreator];
  389. }
  390. }
  391. - (void)sendGroupCreateToAllMembers {
  392. for (Contact *contact in self.members) {
  393. [MessageSender sendGroupCreateMessageForGroup:self toMember:contact];
  394. }
  395. }
  396. - (void)syncGroupInfoToIdentity:(NSString *)identity {
  397. Contact *member = [self contactForMemberIdentity:identity];
  398. if (member) {
  399. [self syncGroupInfoToContact:member];
  400. } else {
  401. // Send empty group create to non-member (but no rename/image/ballots)
  402. Contact *nonMember = [[ContactStore sharedContactStore] contactForIdentity:identity];
  403. [MessageSender sendGroupCreateMessageForGroup:self toMember:nonMember];
  404. }
  405. }
  406. - (void)syncGroupInfoToContact:(Contact *)contact {
  407. [MessageSender sendGroupCreateMessageForGroup:self toMember:contact];
  408. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(500 * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{
  409. [MessageSender sendGroupRenameMessageForConversation:_conversation toMember:contact addSystemMessage:NO];
  410. [MessageSender sendGroupSharedMessagesForConversation:_conversation toMember:contact];
  411. if (_conversation.groupImage != nil) {
  412. GroupPhotoSender *sender = [[GroupPhotoSender alloc] init];
  413. [sender startWithImageData:_conversation.groupImage.data inConversation:_conversation toMember:contact onCompletion:^{} onError:^(NSError *error) {
  414. DDLogError(@"Sending group photo failed: %@", error);
  415. }];
  416. }
  417. });
  418. }
  419. - (void)syncGroupInfoToAll {
  420. [self sendGroupCreateToAllMembers];
  421. [MessageSender sendGroupRenameMessageForConversation:_conversation addSystemMessage:NO];
  422. if (_conversation.groupImage != nil) {
  423. GroupPhotoSender *sender = [[GroupPhotoSender alloc] init];
  424. [sender startWithImageData:_conversation.groupImage.data inConversation:_conversation toMember:nil onCompletion:^{} onError:^(NSError *error) {
  425. DDLogError(@"Sending group photo failed: %@", error);
  426. }];
  427. }
  428. }
  429. - (void)adminDeleteGroup {
  430. [MessageSender sendGroupLeaveMessageForConversation:_conversation];
  431. /* record deleted group if necessary */
  432. if (_conversation.contact != nil) {
  433. [self setGroupState:kGroupStateLeft];
  434. _conversation = nil;
  435. }
  436. }
  437. - (Group *)group {
  438. if (_group == nil) {
  439. EntityManager *entityManager = [[EntityManager alloc] init];
  440. _group = [entityManager.entityCreator group];
  441. _group.groupCreator = _conversation.contact.identity;
  442. _group.groupId = _conversation.groupId;
  443. _group.state = [NSNumber numberWithInt:kGroupStateActive];
  444. }
  445. return _group;
  446. }
  447. - (void)setGroupState:(GroupState)state {
  448. EntityManager *entityManager = [[EntityManager alloc] init];
  449. [entityManager performSyncBlockAndSafe:^{
  450. self.group.state = [NSNumber numberWithInt:state];
  451. }];
  452. }
  453. - (void)remoteAddGroupMember:(NSString*)memberIdentity notify:(BOOL)notify remoteSentDate:remoteSentDate {
  454. if ([memberIdentity isEqualToString:[MyIdentityStore sharedMyIdentityStore].identity]) {
  455. [self setGroupState:kGroupStateActive];
  456. [self postSystemMessageType:kSystemMessageGroupSelfAdded withArg:nil remoteSentDate:remoteSentDate];
  457. return;
  458. }
  459. EntityManager *entityManager = [[EntityManager alloc] init];
  460. Contact *member = [entityManager.entityFetcher contactForId:memberIdentity];
  461. if (member == nil) {
  462. /* must fetch key */
  463. [self fetchPublicKeyForMember:memberIdentity onCompletion:^(Contact *contact) {
  464. [self newGroupMemberPostProcess:contact notify:notify remoteSentDate:remoteSentDate];
  465. }];
  466. } else {
  467. [_conversation addMembersObject:member];
  468. [self newGroupMemberPostProcess:member notify:notify remoteSentDate:remoteSentDate];
  469. }
  470. }
  471. - (void)updateGroupMyIdentity:(NSString *)groupMyIdentity forConversation:(Conversation *)conversation {
  472. if ([groupMyIdentity isEqualToString:conversation.groupMyIdentity]) {
  473. return;
  474. }
  475. EntityManager *entityManager = [[EntityManager alloc] init];
  476. [entityManager performSyncBlockAndSafe:^{
  477. conversation.groupMyIdentity = groupMyIdentity;
  478. }];
  479. }
  480. - (void)remoteGroupMemberLeft:(NSString *)memberIdentity remoteSentDate:remoteSentDate {
  481. Contact *member = [self removeGroupMember:memberIdentity];
  482. if (member) {
  483. [self postSystemMessageForMember:member type:kSystemMessageGroupMemberLeave remoteSentDate:remoteSentDate];
  484. }
  485. }
  486. - (void)remoteRemoveGroupMember:(NSString *)memberIdentity remoteSentDate:remoteSentDate {
  487. if ([memberIdentity isEqualToString:[MyIdentityStore sharedMyIdentityStore].identity]) {
  488. [self setGroupState:kGroupStateForcedLeft];
  489. [self postSystemMessageType:kSystemMessageGroupSelfRemoved withArg:nil remoteSentDate:remoteSentDate];
  490. return;
  491. }
  492. Contact *member = [self removeGroupMember:memberIdentity];
  493. if (member) {
  494. [self postSystemMessageForMember:member type:kSystemMessageGroupMemberForcedLeave remoteSentDate:remoteSentDate];
  495. }
  496. }
  497. - (Contact *)removeGroupMember:(NSString *)memberIdentity {
  498. Contact *member = [self contactForMemberIdentity:memberIdentity];
  499. if (member) {
  500. [_conversation removeMembersObject:member];
  501. }
  502. return member;
  503. }
  504. - (void)newGroupMemberPostProcess:(Contact *)newMember notify:(BOOL)notify remoteSentDate:remoteSentDate {
  505. // send own open ballots
  506. for (Ballot *ballot in _conversation.ballots) {
  507. if (ballot.isOwn && [ballot isClosed] == NO) {
  508. [MessageSender sendGroupSharedMessagesForConversation:_conversation toMember:newMember];
  509. }
  510. }
  511. if (notify) {
  512. [self postSystemMessageForMember:newMember type:kSystemMessageGroupMemberAdd remoteSentDate:remoteSentDate];
  513. }
  514. }
  515. - (void)fetchPublicKeyForMember:(NSString *)identity onCompletion:(void(^)(Contact *contact))onCompletion {
  516. [[ContactStore sharedContactStore] fetchPublicKeyForIdentity:identity onCompletion:^(NSData *publicKey) {
  517. __block Contact *newMember;
  518. EntityManager *entityManager = [[EntityManager alloc] init];
  519. [entityManager performSyncBlockAndSafe:^{
  520. /* add member to group */
  521. newMember = [entityManager.entityFetcher contactForId:identity];
  522. if (newMember != nil) {
  523. /* Check if this member has already been added, as it's possible that it happens twice
  524. due to overlapping asynchronous ID fetches (i.e. multiple group create/update messages
  525. coming in at once) */
  526. if (![[_conversation members] containsObject:newMember]) {
  527. [_conversation addMembersObject:newMember];
  528. }
  529. }
  530. }];
  531. onCompletion(newMember);
  532. } onError:^(NSError *error) {
  533. DDLogWarn(@"Could not fetch public key for %@: %@", identity, error);
  534. }];
  535. }
  536. - (void)postSystemMessageForMember:(Contact*)contact type:(NSInteger)type remoteSentDate:remoteSentDate {
  537. NSData *arg = [contact.displayName dataUsingEncoding:NSUTF8StringEncoding];
  538. [self postSystemMessageType:type withArg:arg remoteSentDate:remoteSentDate];
  539. }
  540. - (void)postSystemMessageType:(NSInteger)type withArg:(NSData *)arg remoteSentDate:(NSDate*)remoteSentDate {
  541. EntityManager *entityManager = [[EntityManager alloc] init];
  542. [entityManager performSyncBlockAndSafe:^{
  543. /* Insert system message to document this change */
  544. SystemMessage *systemMessage = [entityManager.entityCreator systemMessageForConversation:_conversation];
  545. systemMessage.type = [NSNumber numberWithInteger:type];
  546. systemMessage.arg = arg;
  547. systemMessage.remoteSentDate = remoteSentDate;
  548. }];
  549. }
  550. - (NSString *)description {
  551. NSString *result = @"Group ";
  552. result = [result stringByAppendingFormat: @"name: %@\n", self.name];
  553. result = [result stringByAppendingFormat: @"members: %@\n", self.members];
  554. result = [result stringByAppendingFormat: @"conversation: %@\n", _conversation];
  555. result = [result stringByAppendingFormat: @"group: %@\n", _group];
  556. result = [result stringByAppendingFormat: @"lastSyncRequest: %@\n", _lastSyncRequest];
  557. return result;
  558. }
  559. @end