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