Statistics
| Branch: | Tag: | Revision:

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