chatsecureios / ChatSecure / Classes / Controllers / XMPP / OTRXMPPRoomYapStorage.m @ 80684e32
History | View | Annotate | Download (11.4 KB)
1 |
// |
---|---|
2 |
// OTRXMPPRoomYapStorage.m |
3 |
// ChatSecure |
4 |
// |
5 |
// Created by David Chiles on 10/7/15. |
6 |
// Copyright © 2015 Chris Ballinger. All rights reserved. |
7 |
// |
8 |
|
9 |
#import "OTRXMPPRoomYapStorage.h" |
10 |
#import "OTRDatabaseManager.h" |
11 |
#import "OTRAccount.h" |
12 |
#import <ChatSecureCore/ChatSecureCore-Swift.h> |
13 |
#import "OTRLog.h" |
14 |
#import "OTRXMPPManager_Private.h" |
15 |
@import YapDatabase; |
16 |
@import XMPPFramework; |
17 |
|
18 |
@interface OTRXMPPRoomYapStorage () |
19 |
|
20 |
@property (nonatomic) dispatch_queue_t parentQueue; |
21 |
|
22 |
@end |
23 |
|
24 |
@implementation OTRXMPPRoomYapStorage |
25 |
|
26 |
- (instancetype)initWithDatabaseConnection:(YapDatabaseConnection *)databaseConnection |
27 |
{ |
28 |
if (self = [self init]){ |
29 |
self.databaseConnection = databaseConnection; |
30 |
} |
31 |
return self; |
32 |
} |
33 |
|
34 |
- (OTRXMPPRoom *)fetchRoomWithXMPPRoomJID:(NSString *)roomJID accountId:(NSString *)accountId inTransaction:(YapDatabaseReadTransaction *)transaction { |
35 |
return [OTRXMPPRoom fetchObjectWithUniqueID:[OTRXMPPRoom createUniqueId:accountId jid:roomJID] transaction:transaction]; |
36 |
} |
37 |
|
38 |
- (BOOL)existsMessage:(XMPPMessage *)message from:(XMPPJID *)fromJID stanzaId:(nullable NSString*)stanzaId transaction:(YapDatabaseReadTransaction *)transaction |
39 |
{ |
40 |
NSDate *remoteTimestamp = [message delayedDeliveryDate]; |
41 |
if (!remoteTimestamp) |
42 |
{ |
43 |
// When the xmpp server sends us a room message, it will always timestamp delayed messages. |
44 |
// For example, when retrieving the discussion history, all messages will include the original timestamp. |
45 |
// If a message doesn't include such timestamp, then we know we're getting it in "real time". |
46 |
|
47 |
return NO; |
48 |
} |
49 |
NSString *elementID = message.elementID; |
50 |
__block BOOL result = NO; |
51 |
[transaction enumerateMessagesWithElementId:elementID originId:nil stanzaId:stanzaId block:^(id<OTRMessageProtocol> _Nonnull databaseMessage, BOOL * _Null_unspecified stop) { |
52 |
//Need to check room JID |
53 |
//So if message has same ID and same room jid that's got to be the same message, right? |
54 |
if ([databaseMessage isKindOfClass:[OTRXMPPRoomMessage class]]) { |
55 |
OTRXMPPRoomMessage *msg = (OTRXMPPRoomMessage *)databaseMessage; |
56 |
if ([msg.roomJID isEqualToString:fromJID.bare]) { |
57 |
*stop = YES; |
58 |
result = YES; |
59 |
}} |
60 |
}]; |
61 |
return result; |
62 |
} |
63 |
|
64 |
- (void)insertIncomingMessage:(XMPPMessage *)message intoRoom:(XMPPRoom *)room |
65 |
{ |
66 |
NSString *accountId = room.xmppStream.tag; |
67 |
NSString *roomJID = room.roomJID.bare; |
68 |
XMPPJID *fromJID = [message from]; |
69 |
if (!accountId || !roomJID || !fromJID) { |
70 |
return; |
71 |
} |
72 |
__block OTRXMPPRoomMessage *databaseMessage = nil; |
73 |
__block OTRXMPPRoom *databaseRoom = nil; |
74 |
__block OTRXMPPAccount *account = nil; |
75 |
[self.databaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) { |
76 |
account = [OTRXMPPAccount fetchObjectWithUniqueID:accountId transaction:transaction]; |
77 |
// Sends a response receipt when receiving a delivery receipt request |
78 |
[OTRXMPPRoomMessage handleDeliveryReceiptRequestWithMessage:message xmppStream:room.xmppStream]; |
79 |
|
80 |
// Extract XEP-0359 stanza-id |
81 |
NSString *stanzaId = [message extractStanzaIdWithAccount:account]; |
82 |
NSString *originId = message.originId; |
83 |
databaseMessage.originId = originId; |
84 |
databaseMessage.stanzaId = stanzaId; |
85 |
|
86 |
if ([self existsMessage:message from:fromJID stanzaId:stanzaId transaction:transaction]) { |
87 |
// This message already exists and shouldn't be inserted |
88 |
DDLogVerbose(@"%@: %@ - Duplicate MUC message %@", THIS_FILE, THIS_METHOD, message); |
89 |
return; |
90 |
} |
91 |
databaseRoom = [self fetchRoomWithXMPPRoomJID:roomJID accountId:accountId inTransaction:transaction]; |
92 |
if(!databaseRoom) { |
93 |
databaseRoom = [[OTRXMPPRoom alloc] init]; |
94 |
databaseRoom.lastRoomMessageId = @""; // Hack to make it show up in list |
95 |
databaseRoom.accountUniqueId = accountId; |
96 |
databaseRoom.jid = roomJID; |
97 |
} |
98 |
if (databaseRoom.joined && |
99 |
([message elementForName:@"x" xmlns:XMPPMUCUserNamespace] || |
100 |
[message elementForName:@"x" xmlns:@"jabber:x:conference"])) { |
101 |
DDLogWarn(@"Received invitation to current room: %@", message); |
102 |
return; |
103 |
} |
104 |
|
105 |
databaseMessage = [[OTRXMPPRoomMessage alloc] init]; |
106 |
databaseMessage.xmppId = [message elementID]; |
107 |
databaseMessage.messageText = [message body]; |
108 |
NSDate *messageDate = [message delayedDeliveryDate]; |
109 |
if (!messageDate) { |
110 |
messageDate = [NSDate date]; |
111 |
} |
112 |
databaseMessage.messageDate = messageDate; |
113 |
databaseMessage.senderJID = [fromJID full]; |
114 |
databaseMessage.roomJID = databaseRoom.jid; |
115 |
databaseMessage.state = RoomMessageStateReceived; |
116 |
databaseMessage.roomUniqueId = databaseRoom.uniqueId; |
117 |
|
118 |
databaseRoom.lastRoomMessageId = [databaseMessage uniqueId]; |
119 |
NSString *activeThreadYapKey = [[OTRAppDelegate appDelegate] activeThreadYapKey]; |
120 |
if([activeThreadYapKey isEqualToString:databaseMessage.threadId]) { |
121 |
databaseMessage.read = YES; |
122 |
} else { |
123 |
databaseMessage.read = NO; |
124 |
} |
125 |
|
126 |
[databaseRoom saveWithTransaction:transaction]; |
127 |
[databaseMessage saveWithTransaction:transaction]; |
128 |
} completionBlock:^{ |
129 |
if(databaseMessage) { |
130 |
OTRXMPPManager *xmpp = (OTRXMPPManager*)[OTRProtocolManager.shared protocolForAccount:account]; |
131 |
[xmpp.fileTransferManager createAndDownloadItemsIfNeededWithMessage:databaseMessage readConnection:OTRDatabaseManager.shared.readOnlyDatabaseConnection force:NO]; |
132 |
// If delayedDeliveryDate is set we are retrieving history. Don't show |
133 |
// notifications in that case. Also, don't show notifications for archived |
134 |
// rooms. |
135 |
if (!message.delayedDeliveryDate && !databaseRoom.isArchived) { |
136 |
[[UIApplication sharedApplication] showLocalNotification:databaseMessage]; |
137 |
} |
138 |
} |
139 |
}]; |
140 |
} |
141 |
|
142 |
- (id <OTRMessageProtocol>)lastMessageInRoom:(XMPPRoom *)room accountKey:(NSString *)accountKey |
143 |
{ |
144 |
__block id<OTRMessageProtocol> message = nil; |
145 |
[self.databaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { |
146 |
OTRXMPPRoom *databaseRoom = [self fetchRoomWithXMPPRoomJID:room.roomJID.bare accountId:accountKey inTransaction:transaction]; |
147 |
message = [databaseRoom lastMessageWithTransaction:transaction]; |
148 |
}]; |
149 |
return message; |
150 |
} |
151 |
|
152 |
//MARK: XMPPRoomStorage |
153 |
|
154 |
- (BOOL)configureWithParent:(XMPPRoom *)aParent queue:(dispatch_queue_t)queue |
155 |
{ |
156 |
self.parentQueue = queue; |
157 |
return YES; |
158 |
} |
159 |
|
160 |
/** |
161 |
* Updates and returns the occupant for the given presence element. |
162 |
* If the presence type is "available", and the occupant doesn't already exist, then one should be created. |
163 |
**/ |
164 |
- (void)handlePresence:(XMPPPresence *)presence room:(XMPPRoom *)room { |
165 |
NSString *accountId = room.xmppStream.tag; |
166 |
XMPPJID *presenceJID = [presence from]; |
167 |
|
168 |
DDXMLElement *item = nil; |
169 |
DDXMLElement *mucElement = [presence elementForName:@"x" xmlns:XMPPMUCUserNamespace]; |
170 |
if (mucElement) { |
171 |
item = [mucElement elementForName:@"item"]; |
172 |
} |
173 |
if (!item) { |
174 |
return; // Unexpected presence format |
175 |
} |
176 |
|
177 |
XMPPJID *buddyRealJID = nil; |
178 |
NSString *buddyJIDString = [item attributeStringValueForName:@"jid"]; |
179 |
if (buddyJIDString) { |
180 |
// Will be nil in anonymous rooms (and semi-anonymous rooms if we are not moderators) |
181 |
buddyRealJID = [[XMPPJID jidWithString:buddyJIDString] bareJID]; |
182 |
} |
183 |
NSString *buddyRole = [item attributeStringValueForName:@"role"]; |
184 |
NSString *buddyAffiliation = [item attributeStringValueForName:@"affiliation"]; |
185 |
|
186 |
[self.databaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) { |
187 |
|
188 |
OTRXMPPRoomOccupant *occupant = [OTRXMPPRoomOccupant occupantWithJid:presenceJID realJID:buddyRealJID roomJID:room.roomJID accountId:accountId createIfNeeded:YES transaction:transaction]; |
189 |
if ([[presence type] isEqualToString:@"unavailable"]) { |
190 |
occupant.available = NO; |
191 |
} else { |
192 |
occupant.available = YES; |
193 |
} |
194 |
occupant.jid = [presenceJID full]; // Nicknames can change, so update |
195 |
occupant.roomName = [presenceJID resource]; |
196 |
|
197 |
// Role |
198 |
if ([buddyRole isEqualToString:@"moderator"]) { |
199 |
occupant.role = RoomOccupantRoleModerator; |
200 |
} else if ([buddyRole isEqualToString:@"participant"]) { |
201 |
occupant.role = RoomOccupantRoleParticipant; |
202 |
} else if ([buddyRole isEqualToString:@"visitor"]) { |
203 |
occupant.role = RoomOccupantRoleVisitor; |
204 |
} else { |
205 |
occupant.role = RoomOccupantRoleNone; |
206 |
} |
207 |
|
208 |
// Affiliation |
209 |
if ([buddyAffiliation isEqualToString:@"owner"]) { |
210 |
occupant.affiliation = RoomOccupantAffiliationOwner; |
211 |
} else if ([buddyAffiliation isEqualToString:@"admin"]) { |
212 |
occupant.affiliation = RoomOccupantAffiliationAdmin; |
213 |
} else if ([buddyAffiliation isEqualToString:@"member"]) { |
214 |
occupant.affiliation = RoomOccupantAffiliationMember; |
215 |
} else if ([buddyAffiliation isEqualToString:@"outcast"]) { |
216 |
occupant.affiliation = RoomOccupantAffiliationOutcast; |
217 |
} else { |
218 |
occupant.affiliation = RoomOccupantAffiliationNone; |
219 |
} |
220 |
[occupant saveWithTransaction:transaction]; |
221 |
}]; |
222 |
} |
223 |
|
224 |
/** |
225 |
* Stores or otherwise handles the given message element. |
226 |
**/ |
227 |
- (void)handleIncomingMessage:(XMPPMessage *)message room:(XMPPRoom *)room { |
228 |
//DDLogVerbose(@"OTRXMPPRoomYapStorage handleIncomingMessage: %@", message); |
229 |
XMPPJID *myRoomJID = room.myRoomJID; |
230 |
XMPPJID *messageJID = [message from]; |
231 |
|
232 |
if ([myRoomJID isEqualToJID:messageJID]) |
233 |
{ |
234 |
if (![message wasDelayed]) |
235 |
{ |
236 |
// Ignore - we already stored message in handleOutgoingMessage:room: |
237 |
return; |
238 |
} |
239 |
} |
240 |
|
241 |
//May need to check if the message is unique. Unsure if this is a real problem. Look at XMPPRoomCoreDataStorage.m existsMessage: |
242 |
|
243 |
[self insertIncomingMessage:message intoRoom:room]; |
244 |
} |
245 |
- (void)handleOutgoingMessage:(XMPPMessage *)message room:(XMPPRoom *)room { |
246 |
//DDLogVerbose(@"OTRXMPPRoomYapStorage handleOutgoingMessage: %@", message); |
247 |
} |
248 |
|
249 |
/** |
250 |
* Handles leaving the room, which generally means clearing the list of occupants. |
251 |
**/ |
252 |
- (void)handleDidLeaveRoom:(XMPPRoom *)room { |
253 |
NSString *roomJID = room.roomJID.bare; |
254 |
NSString *accountId = room.xmppStream.tag; |
255 |
[self.databaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) { |
256 |
OTRXMPPRoom *databaseRoom = [self fetchRoomWithXMPPRoomJID:roomJID accountId:accountId inTransaction:transaction]; |
257 |
databaseRoom.joined = NO; |
258 |
[databaseRoom saveWithTransaction:transaction]; |
259 |
}]; |
260 |
} |
261 |
|
262 |
- (void)handleDidJoinRoom:(XMPPRoom *)room withNickname:(NSString *)nickname { |
263 |
NSString *roomJID = room.roomJID.bare; |
264 |
NSString *accountId = room.xmppStream.tag; |
265 |
[self.databaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) { |
266 |
OTRXMPPRoom *databaseRoom = [self fetchRoomWithXMPPRoomJID:roomJID accountId:accountId inTransaction:transaction]; |
267 |
|
268 |
databaseRoom.joined = YES; |
269 |
databaseRoom.ownJID = room.myRoomJID.full; |
270 |
[databaseRoom saveWithTransaction:transaction]; |
271 |
}]; |
272 |
} |
273 |
|
274 |
|
275 |
|
276 |
@end |