Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #750 from esl/andres.XMPPMUCLight
Add MUC Light implementation https://xmpp.org/extensions/inbox/muc-light.html
- Loading branch information
Showing
16 changed files
with
2,922 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
Extensions/XMPPMUCLight/CoreDataStorage/XMPPRoomLight.xcdatamodel/contents
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> | ||
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="10171" systemVersion="15E65" minimumToolsVersion="Xcode 7.0"> | ||
<entity name="XMPPRoomLightMessageCoreDataStorageObject" representedClassName="XMPPRoomMessageCoreDataStorageObject" syncable="YES"> | ||
<attribute name="body" optional="YES" attributeType="String" indexed="YES" syncable="YES"/> | ||
<attribute name="fromMe" attributeType="Boolean" defaultValueString="NO" syncable="YES"/> | ||
<attribute name="jid" optional="YES" transient="YES" syncable="YES"/> | ||
<attribute name="jidStr" attributeType="String" indexed="YES" syncable="YES"/> | ||
<attribute name="localTimestamp" attributeType="Date" indexed="YES" syncable="YES"/> | ||
<attribute name="message" optional="YES" transient="YES" syncable="YES"/> | ||
<attribute name="messageStr" optional="YES" attributeType="String" syncable="YES"/> | ||
<attribute name="nickname" optional="YES" attributeType="String" syncable="YES"/> | ||
<attribute name="remoteTimestamp" optional="YES" attributeType="Date" indexed="YES" syncable="YES"/> | ||
<attribute name="roomJID" optional="YES" transient="YES" syncable="YES"/> | ||
<attribute name="roomJIDStr" attributeType="String" indexed="YES" syncable="YES"/> | ||
<attribute name="streamBareJidStr" attributeType="String" indexed="YES" syncable="YES"/> | ||
<attribute name="type" attributeType="Integer 16" defaultValueString="0" syncable="YES"/> | ||
</entity> | ||
<elements> | ||
<element name="XMPPRoomLightMessageCoreDataStorageObject" positionX="-63" positionY="-18" width="128" height="240"/> | ||
</elements> | ||
</model> |
18 changes: 18 additions & 0 deletions
18
Extensions/XMPPMUCLight/CoreDataStorage/XMPPRoomLightCoreDataStorage.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// | ||
// XMPPRoomLightCoreDataStorage.h | ||
// Mangosta | ||
// | ||
// Created by Andres Canal on 6/8/16. | ||
// Copyright © 2016 Inaka. All rights reserved. | ||
// | ||
|
||
#import <XMPPFramework/XMPPFramework.h> | ||
#import <XMPPFramework/XMPPCoreDataStorage.h> | ||
#import "XMPPRoomLight.h" | ||
|
||
@interface XMPPRoomLightCoreDataStorage : XMPPCoreDataStorage <XMPPRoomLightStorage> | ||
|
||
- (void)handleIncomingMessage:(XMPPMessage *)message room:(XMPPRoomLight *)room; | ||
- (void)handleOutgoingMessage:(XMPPMessage *)message room:(XMPPRoomLight *)room; | ||
|
||
@end |
207 changes: 207 additions & 0 deletions
207
Extensions/XMPPMUCLight/CoreDataStorage/XMPPRoomLightCoreDataStorage.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
// | ||
// XMPPRoomLightCoreDataStorage.m | ||
// Mangosta | ||
// | ||
// Created by Andres Canal on 6/8/16. | ||
// Copyright © 2016 Inaka. All rights reserved. | ||
// | ||
|
||
#import "XMPPRoomLightCoreDataStorage.h" | ||
#import "XMPPCoreDataStorageProtected.h" | ||
#import "NSXMLElement+XEP_0203.h" | ||
#import "XMPPRoomLightMessageCoreDataStorageObject.h" | ||
|
||
@implementation XMPPRoomLightCoreDataStorage{ | ||
NSString *messageEntityName; | ||
} | ||
|
||
- (void)commonInit{ | ||
|
||
[super commonInit]; | ||
|
||
messageEntityName = NSStringFromClass([XMPPRoomLightMessageCoreDataStorageObject class]); | ||
|
||
} | ||
|
||
- (void)handleIncomingMessage:(XMPPMessage *)message room:(XMPPRoomLight *)room{ | ||
XMPPStream *xmppStream = room.xmppStream; | ||
|
||
XMPPJID *myRoomJID = [XMPPJID jidWithUser:message.from.user | ||
domain:message.from.domain | ||
resource:xmppStream.myJID.full]; | ||
|
||
XMPPJID *messageJID = [message from]; | ||
|
||
// Ignore - if message is mine and it was not delayed then ignore | ||
// becuase we handled it in handleOutgoingMessage:room: | ||
if ([myRoomJID isEqualToJID:messageJID] && ![message wasDelayed]){ | ||
return; | ||
} | ||
|
||
[self scheduleBlock:^{ | ||
if (![self existsMessage:message forRoom:room stream:xmppStream]){ | ||
[self insertMessage:message outgoing:false forRoom:room stream:xmppStream]; | ||
} | ||
}]; | ||
} | ||
|
||
- (void)handleOutgoingMessage:(XMPPMessage *)message room:(XMPPRoomLight *)room{ | ||
XMPPStream *xmppStream = room.xmppStream; | ||
|
||
[self scheduleBlock:^{ | ||
[self insertMessage:message outgoing:YES forRoom:room stream:xmppStream]; | ||
}]; | ||
} | ||
|
||
- (BOOL)existsMessage:(XMPPMessage *)message forRoom:(XMPPRoomLight *)room stream:(XMPPStream *)xmppStream{ | ||
NSDate *remoteTimestamp = [message delayedDeliveryDate]; | ||
|
||
if (remoteTimestamp == nil){ | ||
// When the xmpp server sends us a room message, it will always timestamp delayed messages. | ||
// For example, when retrieving the discussion history, all messages will include the original timestamp. | ||
// If a message doesn't include such timestamp, then we know we're getting it in "real time". | ||
|
||
return NO; | ||
} | ||
|
||
// Does this message already exist in the database? | ||
// How can we tell if two XMPPRoomMessages are the same? | ||
// | ||
// 1. Same streamBareJidStr | ||
// 2. Same jid | ||
// 3. Same text | ||
// 4. Approximately the same timestamps | ||
// | ||
// This is actually a rather difficult question. | ||
// What if the same user sends the exact same message multiple times? | ||
// | ||
// If we first received the message while already in the room, it won't contain a remoteTimestamp. | ||
// Returning to the room later and downloading the discussion history will return the same message, | ||
// this time with a remote timestamp. | ||
// | ||
// So if the message doesn't have a remoteTimestamp, | ||
// but it's localTimestamp is approximately the same as the remoteTimestamp, | ||
// then this is enough evidence to consider the messages the same. | ||
// | ||
// Note: Predicate order matters. Most unique key should be first, least unique should be last. | ||
|
||
NSManagedObjectContext *moc = [self managedObjectContext]; | ||
NSEntityDescription *messageEntity = [self messageEntity:moc]; | ||
|
||
NSString *streamBareJidStr = [[self myJIDForXMPPStream:xmppStream] bare]; | ||
|
||
XMPPJID *messageJID = [message from]; | ||
NSString *messageBody = [[message elementForName:@"body"] stringValue]; | ||
|
||
NSDate *minLocalTimestamp = [remoteTimestamp dateByAddingTimeInterval:-60]; | ||
NSDate *maxLocalTimestamp = [remoteTimestamp dateByAddingTimeInterval: 60]; | ||
|
||
NSString *predicateFormat = @" body == %@ " | ||
@"AND jidStr == %@ " | ||
@"AND streamBareJidStr == %@ " | ||
@"AND " | ||
@"(" | ||
@" (remoteTimestamp == %@) " | ||
@" OR (remoteTimestamp == NIL && localTimestamp BETWEEN {%@, %@})" | ||
@")"; | ||
|
||
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateFormat, | ||
messageBody, messageJID, streamBareJidStr, | ||
remoteTimestamp, minLocalTimestamp, maxLocalTimestamp]; | ||
|
||
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; | ||
[fetchRequest setEntity:messageEntity]; | ||
[fetchRequest setPredicate:predicate]; | ||
[fetchRequest setFetchLimit:1]; | ||
|
||
NSError *error = nil; | ||
NSArray *results = [moc executeFetchRequest:fetchRequest error:&error]; | ||
|
||
return ([results count] > 0); | ||
} | ||
|
||
- (void)insertMessage:(XMPPMessage *)message | ||
outgoing:(BOOL)isOutgoing | ||
forRoom:(XMPPRoomLight *)room | ||
stream:(XMPPStream *)xmppStream{ | ||
// Extract needed information | ||
|
||
XMPPJID *myRoomJID = [XMPPJID jidWithUser:room.roomJID.user | ||
domain:room.roomJID.domain | ||
resource:xmppStream.myJID.full]; | ||
|
||
XMPPJID *roomJID = room.roomJID; | ||
XMPPJID *messageJID = isOutgoing ? myRoomJID : [message from]; | ||
|
||
NSDate *localTimestamp; | ||
NSDate *remoteTimestamp; | ||
|
||
if (isOutgoing){ | ||
localTimestamp = [[NSDate alloc] init]; | ||
remoteTimestamp = nil; | ||
}else{ | ||
remoteTimestamp = [message delayedDeliveryDate]; | ||
if (remoteTimestamp) { | ||
localTimestamp = remoteTimestamp; | ||
}else{ | ||
localTimestamp = [[NSDate alloc] init]; | ||
} | ||
} | ||
|
||
NSString *messageBody = [[message elementForName:@"body"] stringValue]; | ||
|
||
NSManagedObjectContext *moc = [self managedObjectContext]; | ||
NSString *streamBareJidStr = [[self myJIDForXMPPStream:xmppStream] bare]; | ||
|
||
NSEntityDescription *messageEntity = [self messageEntity:moc]; | ||
|
||
XMPPRoomLightMessageCoreDataStorageObject *roomMessage = (XMPPRoomLightMessageCoreDataStorageObject *) | ||
[[NSManagedObject alloc] initWithEntity:messageEntity insertIntoManagedObjectContext:nil]; | ||
|
||
roomMessage.message = message; | ||
roomMessage.roomJID = roomJID; | ||
roomMessage.jid = messageJID; | ||
roomMessage.nickname = [messageJID resource]; | ||
roomMessage.body = messageBody; | ||
roomMessage.localTimestamp = localTimestamp; | ||
roomMessage.remoteTimestamp = remoteTimestamp; | ||
roomMessage.isFromMe = isOutgoing; | ||
roomMessage.streamBareJidStr = streamBareJidStr; | ||
|
||
[moc insertObject:roomMessage]; | ||
[self didInsertMessage:roomMessage]; | ||
} | ||
|
||
- (void)didInsertMessage:(XMPPRoomLightMessageCoreDataStorageObject *)message{ | ||
// Override me if you're extending the XMPPRoomMessageCoreDataStorageObject class to add additional properties. | ||
// You can update your additional properties here. | ||
// | ||
// At this point the standard properties have already been set. | ||
// So you can, for example, access the XMPPMessage via message.message. | ||
} | ||
|
||
- (NSEntityDescription *)messageEntity:(NSManagedObjectContext *)moc{ | ||
|
||
// This method should be thread-safe. | ||
// So be sure to access the entity name through the property accessor. | ||
|
||
return [NSEntityDescription entityForName:[self messageEntityName] inManagedObjectContext:moc]; | ||
} | ||
|
||
- (NSString *)messageEntityName{ | ||
|
||
__block NSString *result = nil; | ||
|
||
dispatch_block_t block = ^{ | ||
result = messageEntityName; | ||
}; | ||
|
||
if (dispatch_get_specific(storageQueueTag)) | ||
block(); | ||
else | ||
dispatch_sync(storageQueue, block); | ||
|
||
return result; | ||
} | ||
|
||
@end |
46 changes: 46 additions & 0 deletions
46
Extensions/XMPPMUCLight/CoreDataStorage/XMPPRoomLightMessageCoreDataStorageObject.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
#import <Foundation/Foundation.h> | ||
#import <CoreData/CoreData.h> | ||
|
||
#import "XMPPRoomLight.h" | ||
#import "XMPPFramework/XMPPRoomMessage.h" | ||
|
||
@interface XMPPRoomLightMessageCoreDataStorageObject : NSManagedObject <XMPPRoomMessage> | ||
|
||
/** | ||
* The properties below are documented in the XMPPRoomMessage protocol. | ||
**/ | ||
|
||
@property (nonatomic, retain) XMPPMessage * message; // Transient (proper type, not on disk) | ||
@property (nonatomic, retain) NSString * messageStr; // Shadow (binary data, written to disk) | ||
|
||
@property (nonatomic, strong) XMPPJID * roomJID; // Transient (proper type, not on disk) | ||
@property (nonatomic, strong) NSString * roomJIDStr; // Shadow (binary data, written to disk) | ||
|
||
@property (nonatomic, retain) XMPPJID * jid; // Transient (proper type, not on disk) | ||
@property (nonatomic, retain) NSString * jidStr; // Shadow (binary data, written to disk) | ||
|
||
@property (nonatomic, retain) NSString * nickname; | ||
@property (nonatomic, retain) NSString * body; | ||
|
||
@property (nonatomic, retain) NSDate * localTimestamp; | ||
@property (nonatomic, strong) NSDate * remoteTimestamp; | ||
|
||
@property (nonatomic, assign) BOOL isFromMe; | ||
@property (nonatomic, strong) NSNumber * fromMe; | ||
|
||
/** | ||
* The 'type' property can be used to inject event messages. | ||
* For example: "JohnDoe entered the room". | ||
* | ||
* You can define your own types to suit your needs. | ||
* All normal messages will have a type of zero. | ||
**/ | ||
@property (nonatomic, strong) NSNumber * type; | ||
|
||
/** | ||
* If a single instance of XMPPRoomCoreDataStorage is shared between multiple xmppStream's, | ||
* this may be needed to distinguish between the streams. | ||
**/ | ||
@property (nonatomic, strong) NSString *streamBareJidStr; | ||
|
||
@end |
Oops, something went wrong.