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