Statistics
| Branch: | Tag: | Revision:

chatsecureios / ChatSecure / Classes / Controllers / XMPP / OTRXMPPRoomYapStorage.m @ 74191bbf

History | View | Annotate | Download (10.6 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
        occupant.role = [RoomOccupantRoleHelper roleWithString:buddyRole];
199

    
200
        // Affiliation
201
        occupant.affiliation = [RoomOccupantAffiliationHelper affiliationWithString:buddyAffiliation];
202
        [occupant saveWithTransaction:transaction];
203
    }];
204
}
205

    
206
/**
207
 * Stores or otherwise handles the given message element.
208
 **/
209
- (void)handleIncomingMessage:(XMPPMessage *)message room:(XMPPRoom *)room {
210
    //DDLogVerbose(@"OTRXMPPRoomYapStorage handleIncomingMessage: %@", message);
211
    XMPPJID *myRoomJID = room.myRoomJID;
212
    XMPPJID *messageJID = [message from];
213
    
214
    if ([myRoomJID isEqualToJID:messageJID])
215
    {
216
        if (![message wasDelayed])
217
        {
218
            // Ignore - we already stored message in handleOutgoingMessage:room:
219
            return;
220
        }
221
    }
222
    
223
    //May need to check if the message is unique. Unsure if this is a real problem. Look at XMPPRoomCoreDataStorage.m existsMessage:
224
    
225
    [self insertIncomingMessage:message intoRoom:room];
226
}
227
- (void)handleOutgoingMessage:(XMPPMessage *)message room:(XMPPRoom *)room {
228
    //DDLogVerbose(@"OTRXMPPRoomYapStorage handleOutgoingMessage: %@", message);
229
}
230

    
231
/**
232
 * Handles leaving the room, which generally means clearing the list of occupants.
233
 **/
234
- (void)handleDidLeaveRoom:(XMPPRoom *)room {
235
    NSString *roomJID = room.roomJID.bare;
236
    NSString *accountId = room.xmppStream.tag;
237
    [self.databaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) {
238
        OTRXMPPRoom *databaseRoom = [self fetchRoomWithXMPPRoomJID:roomJID accountId:accountId inTransaction:transaction];
239
        databaseRoom.joined = NO;
240
        [databaseRoom saveWithTransaction:transaction];
241
    }];
242
}
243

    
244
- (void)handleDidJoinRoom:(XMPPRoom *)room withNickname:(NSString *)nickname {
245
    NSString *roomJID = room.roomJID.bare;
246
    NSString *accountId = room.xmppStream.tag;
247
    [self.databaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) {
248
        OTRXMPPRoom *databaseRoom = [self fetchRoomWithXMPPRoomJID:roomJID accountId:accountId inTransaction:transaction];
249
        
250
        databaseRoom.joined = YES;
251
        databaseRoom.ownJID = room.myRoomJID.full;
252
        [databaseRoom saveWithTransaction:transaction];
253
    }];
254
}
255

    
256

    
257

    
258
@end