chatsecureios / ChatSecure / Classes / Controllers / XMPP / OTRXMPPRoomManager.m @ 74191bbf
History | View | Annotate | Download (22.5 KB)
1 |
// |
---|---|
2 |
// OTRXMPPRoomManager.m |
3 |
// ChatSecure |
4 |
// |
5 |
// Created by David Chiles on 10/9/15. |
6 |
// Copyright © 2015 Chris Ballinger. All rights reserved. |
7 |
// |
8 |
|
9 |
#import "OTRXMPPRoomManager.h" |
10 |
@import XMPPFramework; |
11 |
#import <ChatSecureCore/ChatSecureCore-Swift.h> |
12 |
#import "OTRXMPPRoomYapStorage.h" |
13 |
#import "OTRBuddy.h" |
14 |
@import YapDatabase; |
15 |
#import "OTRLog.h" |
16 |
|
17 |
|
18 |
@interface OTRXMPPRoomManager () <XMPPMUCDelegate, XMPPRoomDelegate, XMPPStreamDelegate, OTRYapViewHandlerDelegateProtocol> |
19 |
|
20 |
@property (nonatomic, strong, readonly) NSMutableDictionary<XMPPJID*,XMPPRoom*> *rooms; |
21 |
|
22 |
@property (nonatomic, strong, readonly) XMPPMUC *mucModule; |
23 |
|
24 |
/** This dictionary has jid as the key and array of buddy unique Ids to invite once we've joined the room*/ |
25 |
@property (nonatomic, strong, readonly) NSMutableDictionary<NSString*,NSArray<NSString *> *> *inviteDictionary; |
26 |
|
27 |
/** This dictionary is a temporary holding for setting a room subject. Once the room is created teh subject is set from this dictionary. */ |
28 |
@property (nonatomic, strong, readonly) NSMutableDictionary<NSString*,NSString*> *tempRoomSubject; |
29 |
|
30 |
/** This array is a temporary holding with rooms we should configure once connected */ |
31 |
@property (nonatomic, strong, readonly) NSMutableArray<NSString*> *roomsToConfigure; |
32 |
|
33 |
@end |
34 |
|
35 |
@implementation OTRXMPPRoomManager |
36 |
|
37 |
- (instancetype)init { |
38 |
if (self = [super init]) { |
39 |
_mucModule = [[XMPPMUC alloc] init]; |
40 |
_inviteDictionary = [[NSMutableDictionary alloc] init]; |
41 |
_tempRoomSubject = [[NSMutableDictionary alloc] init]; |
42 |
_roomsToConfigure = [[NSMutableArray alloc] init]; |
43 |
_rooms = [[NSMutableDictionary alloc] init]; |
44 |
_bookmarksModule = [[XMPPBookmarksModule alloc] initWithMode:XMPPBookmarksModePrivateXmlStorage dispatchQueue:nil]; |
45 |
} |
46 |
return self; |
47 |
} |
48 |
|
49 |
- (BOOL)activate:(XMPPStream *)aXmppStream |
50 |
{ |
51 |
BOOL result = [super activate:aXmppStream]; |
52 |
[self.mucModule activate:aXmppStream]; |
53 |
[self.mucModule addDelegate:self delegateQueue:moduleQueue]; |
54 |
[multicastDelegate addDelegate:self delegateQueue:moduleQueue]; |
55 |
|
56 |
[self.bookmarksModule activate:self.xmppStream]; |
57 |
|
58 |
//Register view for sending message queue and occupants |
59 |
[self.databaseConnection.database asyncRegisterGroupOccupantsView:nil completionBlock:nil]; |
60 |
|
61 |
return result; |
62 |
} |
63 |
|
64 |
- (void) deactivate { |
65 |
[self.mucModule removeDelegate:self]; |
66 |
[self.mucModule deactivate]; |
67 |
[self.bookmarksModule deactivate]; |
68 |
[super deactivate]; |
69 |
} |
70 |
|
71 |
- (NSString *)joinRoom:(XMPPJID *)jid withNickname:(NSString *)name subject:(NSString *)subject password:(nullable NSString *)password |
72 |
{ |
73 |
dispatch_async(moduleQueue, ^{ |
74 |
if ([subject length]) { |
75 |
[self.tempRoomSubject setObject:subject forKey:jid.bare]; |
76 |
} |
77 |
}); |
78 |
|
79 |
XMPPRoom *room = [self roomForJID:jid]; |
80 |
NSString* accountId = self.xmppStream.tag; |
81 |
NSString *databaseRoomKey = [OTRXMPPRoom createUniqueId:accountId jid:jid.bare]; |
82 |
__block NSString *nickname = name; |
83 |
|
84 |
// Already joined? Can happen if we have auto-join bookmarks. |
85 |
if (room && room.isJoined) { |
86 |
return databaseRoomKey; |
87 |
} |
88 |
|
89 |
if (!room) { |
90 |
OTRXMPPRoomYapStorage *storage = [[OTRXMPPRoomYapStorage alloc] initWithDatabaseConnection:self.databaseConnection]; |
91 |
room = [[XMPPRoom alloc] initWithRoomStorage:storage jid:jid]; |
92 |
[self setRoom:room forJID:room.roomJID]; |
93 |
[room activate:self.xmppStream]; |
94 |
[room addDelegate:self delegateQueue:moduleQueue]; |
95 |
} |
96 |
|
97 |
/** Create room database object */ |
98 |
[self.databaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) { |
99 |
OTRXMPPRoom *room = [[OTRXMPPRoom fetchObjectWithUniqueID:databaseRoomKey transaction:transaction] copy]; |
100 |
if(!room) { |
101 |
room = [[OTRXMPPRoom alloc] init]; |
102 |
room.lastRoomMessageId = @""; // Hack to make it show up in list |
103 |
room.accountUniqueId = accountId; |
104 |
room.jid = jid.bare; |
105 |
} else { |
106 |
// Clear out roles, we'll getpresence updates once we join |
107 |
[self clearOccupantRolesInRoom:room withTransaction:transaction]; |
108 |
} |
109 |
|
110 |
//Other Room properties should be set here |
111 |
if ([subject length]) { |
112 |
room.subject = subject; |
113 |
} |
114 |
room.roomPassword = password; |
115 |
|
116 |
[room saveWithTransaction:transaction]; |
117 |
|
118 |
if (!nickname) { |
119 |
OTRXMPPAccount *account = [OTRXMPPAccount fetchObjectWithUniqueID:accountId transaction:transaction]; |
120 |
nickname = account.bareJID.user; |
121 |
} |
122 |
}]; |
123 |
|
124 |
//Get history if any |
125 |
NSXMLElement *historyElement = nil; |
126 |
OTRXMPPRoomYapStorage *storage = room.xmppRoomStorage; |
127 |
id<OTRMessageProtocol> lastMessage = [storage lastMessageInRoom:room accountKey:accountId]; |
128 |
NSDate *lastMessageDate = [lastMessage messageDate]; |
129 |
if (lastMessageDate) { |
130 |
//Use since as our history marker if we have a last message |
131 |
//http://xmpp.org/extensions/xep-0045.html#enter-managehistory |
132 |
NSString *dateTimeString = [lastMessageDate xmppDateTimeString]; |
133 |
historyElement = [NSXMLElement elementWithName:@"history"]; |
134 |
[historyElement addAttributeWithName:@"since" stringValue:dateTimeString]; |
135 |
} |
136 |
|
137 |
[room joinRoomUsingNickname:nickname history:historyElement password:password]; |
138 |
return databaseRoomKey; |
139 |
} |
140 |
|
141 |
- (void)leaveRoom:(nonnull XMPPJID *)jid |
142 |
{ |
143 |
XMPPRoom *room = [self roomForJID:jid]; |
144 |
[room leaveRoom]; |
145 |
[self removeRoomForJID:jid]; |
146 |
[room removeDelegate:self]; |
147 |
[room deactivate]; |
148 |
} |
149 |
|
150 |
- (void)clearOccupantRolesInRoom:(OTRXMPPRoom *)room withTransaction:(YapDatabaseReadWriteTransaction * _Nonnull)transaction { |
151 |
//Enumerate of room eges to occupants |
152 |
NSString *extensionName = [YapDatabaseConstants extensionName:DatabaseExtensionNameRelationshipExtensionName]; |
153 |
[[transaction ext:extensionName] enumerateEdgesWithName:[OTRXMPPRoomOccupant roomEdgeName] destinationKey:room.uniqueId collection:[OTRXMPPRoom collection] usingBlock:^(YapDatabaseRelationshipEdge *edge, BOOL *stop) { |
154 |
|
155 |
OTRXMPPRoomOccupant *occupant = [transaction objectForKey:edge.sourceKey inCollection:edge.sourceCollection]; |
156 |
occupant.role = RoomOccupantRoleNone; |
157 |
[occupant saveWithTransaction:transaction]; |
158 |
}]; |
159 |
} |
160 |
|
161 |
- (NSString *)startGroupChatWithBuddies:(NSArray<NSString *> *)buddiesArray roomJID:(XMPPJID *)roomName nickname:(NSString *)name subject:(nullable NSString *)subject |
162 |
{ |
163 |
if (buddiesArray.count) { |
164 |
[self performBlockAsync:^{ |
165 |
[self.inviteDictionary setObject:buddiesArray forKey:roomName.bare]; |
166 |
}]; |
167 |
} |
168 |
[self.roomsToConfigure addObject:roomName.bare]; |
169 |
XMPPConferenceBookmark *bookmark = [[XMPPConferenceBookmark alloc] initWithJID:roomName bookmarkName:subject nick:name autoJoin:YES]; |
170 |
[self.bookmarksModule fetchAndPublishWithBookmarksToAdd:@[bookmark] bookmarksToRemove:nil completion:^(NSArray<id<XMPPBookmark>> * _Nullable newBookmarks, XMPPIQ * _Nullable responseIq) { |
171 |
if (newBookmarks) { |
172 |
DDLogInfo(@"Joined new room, added to merged bookmarks: %@", newBookmarks); |
173 |
} |
174 |
} completionQueue:nil]; |
175 |
return [self joinRoom:roomName withNickname:name subject:subject password:nil]; |
176 |
} |
177 |
|
178 |
- (void)inviteBuddies:(NSArray<NSString *> *)buddyUniqueIds toRoom:(XMPPRoom *)room { |
179 |
if (!buddyUniqueIds.count) { |
180 |
return; |
181 |
} |
182 |
NSMutableArray<XMPPJID*> *buddyJIDs = [NSMutableArray arrayWithCapacity:buddyUniqueIds.count]; |
183 |
[self.databaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { |
184 |
[buddyUniqueIds enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { |
185 |
OTRXMPPBuddy *buddy = [OTRXMPPBuddy fetchObjectWithUniqueID:obj transaction:transaction]; |
186 |
XMPPJID *buddyJID = buddy.bareJID; |
187 |
if (buddyJID) { |
188 |
[buddyJIDs addObject:buddyJID]; |
189 |
} |
190 |
}]; |
191 |
}]; |
192 |
// XMPPRoom.inviteUsers doesn't seem to work, so you have |
193 |
// to send an individual invitation for each person. |
194 |
[buddyJIDs enumerateObjectsUsingBlock:^(XMPPJID * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { |
195 |
[room inviteUser:obj withMessage:nil]; |
196 |
}]; |
197 |
} |
198 |
|
199 |
#pragma - mark XMPPStreamDelegate Methods |
200 |
|
201 |
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender { |
202 |
|
203 |
//Once we've connecected and authenticated we find what room services are available |
204 |
[self.mucModule discoverServices]; |
205 |
//Once we've authenitcated we need to rejoin existing rooms |
206 |
NSMutableArray <OTRXMPPRoom *>*roomArray = [[NSMutableArray alloc] init]; |
207 |
__block NSString *nickname = self.xmppStream.myJID.user; |
208 |
[self.databaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { |
209 |
OTRXMPPAccount *account = [OTRXMPPAccount accountForStream:sender transaction:transaction]; |
210 |
if (account) { |
211 |
nickname = account.displayName; |
212 |
} |
213 |
[transaction enumerateKeysAndObjectsInCollection:[OTRXMPPRoom collection] usingBlock:^(NSString * _Nonnull key, id _Nonnull object, BOOL * _Nonnull stop) { |
214 |
|
215 |
if ([object isKindOfClass:[OTRXMPPRoom class]]) { |
216 |
OTRXMPPRoom *room = (OTRXMPPRoom *)object; |
217 |
if ([room.jid length]) { |
218 |
[roomArray addObject:room]; |
219 |
} |
220 |
} |
221 |
|
222 |
} withFilter:^BOOL(NSString * _Nonnull key) { |
223 |
//OTRXMPPRoom is saved with the jid and account id as part of the key |
224 |
if ([key containsString:sender.tag]) { |
225 |
return YES; |
226 |
} |
227 |
return NO; |
228 |
}]; |
229 |
}]; |
230 |
[roomArray enumerateObjectsUsingBlock:^(OTRXMPPRoom * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { |
231 |
[self joinRoom:[XMPPJID jidWithString:obj.jid] withNickname:nickname subject:obj.subject password:obj.roomPassword]; |
232 |
}]; |
233 |
|
234 |
[self addRoomsToBookmarks:roomArray]; |
235 |
|
236 |
[self.bookmarksModule fetchBookmarks:^(NSArray<id<XMPPBookmark>> * _Nullable bookmarks, XMPPIQ * _Nullable responseIq) { |
237 |
|
238 |
} completionQueue:nil]; |
239 |
} |
240 |
|
241 |
- (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error |
242 |
{ |
243 |
//Check id and mark as needs sending |
244 |
|
245 |
} |
246 |
|
247 |
- (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message |
248 |
{ |
249 |
//Check id and mark as sent |
250 |
} |
251 |
|
252 |
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message |
253 |
{ |
254 |
XMPPJID *from = [message from]; |
255 |
//Check that this is a message for one of our rooms |
256 |
if([message isGroupChatMessageWithSubject] && [self roomForJID:from] != nil) { |
257 |
|
258 |
NSString *subject = [message subject]; |
259 |
|
260 |
NSString *databaseRoomKey = [OTRXMPPRoom createUniqueId:self.xmppStream.tag jid:from.bare]; |
261 |
|
262 |
[self.databaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) { |
263 |
OTRXMPPRoom *room = [OTRXMPPRoom fetchObjectWithUniqueID:databaseRoomKey transaction:transaction]; |
264 |
room.subject = subject; |
265 |
[room saveWithTransaction:transaction]; |
266 |
}]; |
267 |
|
268 |
} |
269 |
|
270 |
// Handle group chat message receipts |
271 |
[OTRXMPPRoomMessage handleDeliveryReceiptResponseWithMessage:message writeConnection:self.databaseConnection]; |
272 |
} |
273 |
|
274 |
#pragma - mark XMPPMUCDelegate Methods |
275 |
|
276 |
- (void)xmppMUC:(XMPPMUC *)sender didDiscoverServices:(NSArray *)services |
277 |
{ |
278 |
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[services count]]; |
279 |
[services enumerateObjectsUsingBlock:^(NSXMLElement * _Nonnull element, NSUInteger idx, BOOL * _Nonnull stop) { |
280 |
NSString *jid = [element attributeStringValueForName:@"jid"]; |
281 |
if ([jid length] && [jid containsString:@"conference"]) { |
282 |
[array addObject:jid]; |
283 |
//TODO instead of just checking if it has the word 'confernce' in the name we need to preform a iq 'get' to see it's capabilities. |
284 |
|
285 |
} |
286 |
|
287 |
}]; |
288 |
_conferenceServicesJID = array; |
289 |
} |
290 |
|
291 |
- (void)xmppMUC:(XMPPMUC *)sender roomJID:(XMPPJID *)roomJID didReceiveInvitation:(XMPPMessage *)message |
292 |
{ |
293 |
// We must check if we trust the person who invited us |
294 |
// because some servers will send you invites from anyone |
295 |
// We should probably move some of this code upstream into XMPPFramework |
296 |
|
297 |
// Since XMPP is super great, there are (at least) two ways to receive a room invite. |
298 |
|
299 |
// Examples from XEP-0045: |
300 |
// Example 124. Room Sends Invitation to New Member: |
301 |
// |
302 |
// <message from='darkcave@chat.shakespeare.lit' to='hecate@shakespeare.lit'> |
303 |
// <x xmlns='http://jabber.org/protocol/muc#user'> |
304 |
// <invite from='bard@shakespeare.lit'/> |
305 |
// <password>cauldronburn</password> |
306 |
// </x> |
307 |
// </message> |
308 |
// |
309 |
|
310 |
// Examples from XEP-0249: |
311 |
// |
312 |
// |
313 |
// Example 1. A direct invitation |
314 |
// |
315 |
// <message from='crone1@shakespeare.lit/desktop' to='hecate@shakespeare.lit'> |
316 |
// <x xmlns='jabber:x:conference' |
317 |
// jid='darkcave@macbeth.shakespeare.lit' |
318 |
// password='cauldronburn' |
319 |
// reason='Hey Hecate, this is the place for all good witches!'/> |
320 |
// </message> |
321 |
|
322 |
XMPPJID *fromJID = nil; |
323 |
NSString *password = nil; |
324 |
|
325 |
NSXMLElement * roomInvite = [message elementForName:@"x" xmlns:XMPPMUCUserNamespace]; |
326 |
NSXMLElement * directInvite = [message elementForName:@"x" xmlns:@"jabber:x:conference"]; |
327 |
if (roomInvite) { |
328 |
// XEP-0045 |
329 |
NSXMLElement * invite = [roomInvite elementForName:@"invite"]; |
330 |
fromJID = [XMPPJID jidWithString:[invite attributeStringValueForName:@"from"]]; |
331 |
password = [roomInvite elementForName:@"password"].stringValue; |
332 |
} else if (directInvite) { |
333 |
// XEP-0249 |
334 |
fromJID = [message from]; |
335 |
password = [directInvite attributeStringValueForName:@"password"]; |
336 |
} |
337 |
if (!fromJID) { |
338 |
DDLogWarn(@"Could not parse fromJID from room invite: %@", message); |
339 |
return; |
340 |
} |
341 |
__block OTRXMPPBuddy *buddy = nil; |
342 |
XMPPStream *stream = self.xmppStream; |
343 |
NSString *accountUniqueId = stream.tag; |
344 |
__block NSString *nickname = stream.myJID.user; |
345 |
[self.databaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { |
346 |
OTRXMPPAccount *account = [OTRXMPPAccount accountForStream:stream transaction:transaction]; |
347 |
if (account) { |
348 |
nickname = account.displayName; |
349 |
} |
350 |
buddy = [OTRXMPPBuddy fetchBuddyWithJid:fromJID accountUniqueId:accountUniqueId transaction:transaction]; |
351 |
}]; |
352 |
// We were invited by someone not on our roster. Shady business! |
353 |
if (!buddy) { |
354 |
DDLogWarn(@"Received room invitation from someone not on our roster! %@ %@", fromJID, message); |
355 |
return; |
356 |
} |
357 |
[self joinRoom:roomJID withNickname:nickname subject:nil password:password]; |
358 |
} |
359 |
|
360 |
#pragma - mark XMPPRoomDelegate Methods |
361 |
|
362 |
- (void) xmppRoom:(XMPPRoom *)room didFetchMembersList:(NSArray<NSXMLElement*> *)items { |
363 |
DDLogInfo(@"Fetched members list: %@", items); |
364 |
[self xmppRoom:room addOccupantItems:items]; |
365 |
} |
366 |
|
367 |
- (void)xmppRoom:(XMPPRoom *)room didFetchModeratorsList:(NSArray *)items { |
368 |
DDLogInfo(@"Fetched moderators list: %@", items); |
369 |
[self xmppRoom:room addOccupantItems:items]; |
370 |
} |
371 |
|
372 |
- (void) xmppRoom:(XMPPRoom *)room addOccupantItems:(NSArray<NSXMLElement*> *)items { |
373 |
NSString *accountId = room.xmppStream.tag; |
374 |
[self.databaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) { |
375 |
[items enumerateObjectsUsingBlock:^(NSXMLElement *item, NSUInteger idx, BOOL * _Nonnull stop) { |
376 |
NSString *jidString = [item attributeStringValueForName:@"jid"]; |
377 |
XMPPJID *jid = [XMPPJID jidWithString:jidString]; |
378 |
NSString *affiliation = [item attributeStringValueForName:@"affiliation"]; |
379 |
|
380 |
// jid and affiliation MUST be included |
381 |
if (!jid || !affiliation) { return; } |
382 |
|
383 |
// Make sure occupant object exists/is created |
384 |
OTRXMPPRoomOccupant *occupant = [OTRXMPPRoomOccupant occupantWithJid:jid realJID:jid roomJID:room.roomJID accountId:accountId createIfNeeded:YES transaction:transaction]; |
385 |
occupant.affiliation = [RoomOccupantAffiliationHelper affiliationWithString:affiliation]; |
386 |
|
387 |
// Role MAY be included, so get that if it's there |
388 |
NSString *role = [item attributeStringValueForName:@"role"]; |
389 |
if (role) { |
390 |
occupant.role = [RoomOccupantRoleHelper roleWithString:role]; |
391 |
} |
392 |
|
393 |
[occupant saveWithTransaction:transaction]; |
394 |
}]; |
395 |
}]; |
396 |
} |
397 |
|
398 |
- (void)xmppRoomDidCreate:(XMPPRoom *)sender { |
399 |
[self.roomsToConfigure removeObject:sender.roomJID.bare]; |
400 |
[sender fetchConfigurationForm]; |
401 |
} |
402 |
|
403 |
- (void)xmppRoom:(XMPPRoom *)sender didFetchConfigurationForm:(DDXMLElement *)configForm { |
404 |
[sender configureRoomUsingOptions:[[self class] defaultRoomConfiguration]]; |
405 |
} |
406 |
|
407 |
- (void)xmppRoom:(XMPPRoom *)sender didConfigure:(XMPPIQ *)iqResult { |
408 |
//Set Room Subject |
409 |
NSString *subject = [self.tempRoomSubject objectForKey:sender.roomJID.bare]; |
410 |
if (subject) { |
411 |
[self.tempRoomSubject removeObjectForKey:sender.roomJID.bare]; |
412 |
[sender changeRoomSubject:subject]; |
413 |
} |
414 |
|
415 |
//Invite buddies |
416 |
NSArray<NSString*> *buddyUniqueIds = [self.inviteDictionary objectForKey:sender.roomJID.bare]; |
417 |
if (buddyUniqueIds) { |
418 |
[self.inviteDictionary removeObjectForKey:sender.roomJID.bare]; |
419 |
[self inviteBuddies:buddyUniqueIds toRoom:sender]; |
420 |
} |
421 |
|
422 |
// Fetch member list. Ideally this would be done after the invites above have been sent to the network, but the messages pass all kinds of async delegates before they are actually sent, so unfortunately we can't wait for that. |
423 |
[self performBlockAsync:^{ |
424 |
[sender fetchMembersList]; |
425 |
[sender fetchModeratorsList]; |
426 |
}]; |
427 |
} |
428 |
|
429 |
- (void)xmppRoomDidJoin:(XMPPRoom *)sender |
430 |
{ |
431 |
// Older prosody servers have a bug where they consider all room as already |
432 |
// existing, so the status 201 is never sent. |
433 |
if ([self.roomsToConfigure containsObject:sender.roomJID.bare]) { |
434 |
[self xmppRoomDidCreate:sender]; |
435 |
} else { |
436 |
// Fetch member list |
437 |
[self performBlockAsync:^{ |
438 |
[sender fetchMembersList]; |
439 |
[sender fetchModeratorsList]; |
440 |
}]; |
441 |
} |
442 |
} |
443 |
|
444 |
- (void)xmppRoomDidLeave:(XMPPRoom *)sender { |
445 |
NSString *databaseRoomKey = [OTRXMPPRoom createUniqueId:self.xmppStream.tag jid:[sender.roomJID bare]]; |
446 |
[self.databaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) { |
447 |
OTRXMPPRoom *room = [OTRXMPPRoom fetchObjectWithUniqueID:databaseRoomKey transaction:transaction]; |
448 |
if (room) { |
449 |
[self clearOccupantRolesInRoom:room withTransaction:transaction]; |
450 |
} |
451 |
}]; |
452 |
} |
453 |
|
454 |
#pragma mark - Utility |
455 |
|
456 |
- (void) removeRoomForJID:(nonnull XMPPJID*)jid { |
457 |
NSParameterAssert(jid != nil); |
458 |
if (!jid) { return; } |
459 |
[self performBlockAsync:^{ |
460 |
[self.rooms removeObjectForKey:jid.bareJID]; |
461 |
}]; |
462 |
} |
463 |
|
464 |
- (void) setRoom:(nonnull XMPPRoom*)room forJID:(nonnull XMPPJID*)jid { |
465 |
NSParameterAssert(room != nil); |
466 |
NSParameterAssert(jid != nil); |
467 |
if (!room || !jid) { |
468 |
return; |
469 |
} |
470 |
[self performBlockAsync:^{ |
471 |
[self.rooms setObject:room forKey:jid.bareJID]; |
472 |
}]; |
473 |
} |
474 |
|
475 |
- (nullable XMPPRoom*) roomForJID:(nonnull XMPPJID*)jid { |
476 |
NSParameterAssert(jid != nil); |
477 |
if (!jid) { return nil; } |
478 |
__block XMPPRoom *room = nil; |
479 |
[self performBlock:^{ |
480 |
room = [self.rooms objectForKey:jid.bareJID]; |
481 |
}]; |
482 |
return room; |
483 |
} |
484 |
|
485 |
#pragma - mark Class Methods |
486 |
|
487 |
+ (NSXMLElement *)defaultRoomConfiguration |
488 |
{ |
489 |
NSXMLElement *form = [[NSXMLElement alloc] initWithName:@"x" xmlns:@"jabber:x:data"]; |
490 |
|
491 |
NSXMLElement *formTypeField = [[NSXMLElement alloc] initWithName:@"field"]; |
492 |
[formTypeField addAttributeWithName:@"var" stringValue:@"FORM_TYPE"]; |
493 |
[formTypeField addChild:[[NSXMLElement alloc] initWithName:@"value" stringValue:@"http://jabber.org/protocol/muc#roomconfig"]]; |
494 |
|
495 |
NSXMLElement *publicField = [[NSXMLElement alloc] initWithName:@"field"]; |
496 |
[publicField addAttributeWithName:@"var" stringValue:@"muc#roomconfig_publicroom"]; |
497 |
[publicField addChild:[[NSXMLElement alloc] initWithName:@"value" numberValue:@(0)]]; |
498 |
|
499 |
NSXMLElement *persistentField = [[NSXMLElement alloc] initWithName:@"field"]; |
500 |
[persistentField addAttributeWithName:@"var" stringValue:@"muc#roomconfig_persistentroom"]; |
501 |
[persistentField addChild:[[NSXMLElement alloc] initWithName:@"value" numberValue:@(1)]]; |
502 |
|
503 |
NSXMLElement *whoisField = [[NSXMLElement alloc] initWithName:@"field"]; |
504 |
[whoisField addAttributeWithName:@"var" stringValue:@"muc#roomconfig_whois"]; |
505 |
[whoisField addChild:[[NSXMLElement alloc] initWithName:@"value" stringValue:@"anyone"]]; |
506 |
|
507 |
NSXMLElement *membersOnlyField = [[NSXMLElement alloc] initWithName:@"field"]; |
508 |
[membersOnlyField addAttributeWithName:@"var" stringValue:@"muc#roomconfig_membersonly"]; |
509 |
[membersOnlyField addChild:[[NSXMLElement alloc] initWithName:@"value" numberValue:@(1)]]; |
510 |
|
511 |
NSXMLElement *getMemberListField = [[NSXMLElement alloc] initWithName:@"field"]; |
512 |
[getMemberListField addAttributeWithName:@"var" stringValue:@"muc#roomconfig_getmemberlist"]; |
513 |
[getMemberListField addChild:[[NSXMLElement alloc] initWithName:@"value" stringValue:@"moderator"]]; |
514 |
[getMemberListField addChild:[[NSXMLElement alloc] initWithName:@"value" stringValue:@"participant"]]; |
515 |
|
516 |
NSXMLElement *presenceBroadcastField = [[NSXMLElement alloc] initWithName:@"field"]; |
517 |
[presenceBroadcastField addAttributeWithName:@"var" stringValue:@"muc#roomconfig_presencebroadcast"]; |
518 |
[presenceBroadcastField addChild:[[NSXMLElement alloc] initWithName:@"value" stringValue:@"moderator"]]; |
519 |
[presenceBroadcastField addChild:[[NSXMLElement alloc] initWithName:@"value" stringValue:@"participant"]]; |
520 |
|
521 |
[form addChild:formTypeField]; |
522 |
[form addChild:publicField]; |
523 |
[form addChild:persistentField]; |
524 |
[form addChild:whoisField]; |
525 |
[form addChild:membersOnlyField]; |
526 |
[form addChild:getMemberListField]; |
527 |
[form addChild:presenceBroadcastField]; |
528 |
|
529 |
return form; |
530 |
} |
531 |
|
532 |
@end |
533 |
|
534 |
@implementation XMPPRoom(RoomManager) |
535 |
- (void) sendRoomMessage:(OTRXMPPRoomMessage *)roomMessage { |
536 |
NSParameterAssert(roomMessage); |
537 |
if (!roomMessage) { return; } |
538 |
NSString *elementId = roomMessage.xmppId; |
539 |
if (!elementId.length) { |
540 |
elementId = roomMessage.uniqueId; |
541 |
} |
542 |
NSXMLElement *body = [NSXMLElement elementWithName:@"body" stringValue:roomMessage.text]; |
543 |
// type=groupchat and to=room.full are set inside XMPPRoom.sendMessage |
544 |
XMPPMessage *message = [XMPPMessage messageWithType:nil elementID:roomMessage.xmppId child:body]; |
545 |
[message addReceiptRequest]; |
546 |
[self sendMessage:message]; |
547 |
} |
548 |
@end |