Skip to content

Commit

Permalink
Merge PRs #998 #1001 #1002 #1003
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisballinger committed Nov 13, 2017
2 parents 0ec4785 + baf95bf commit 60fa06c
Show file tree
Hide file tree
Showing 19 changed files with 950 additions and 21 deletions.
3 changes: 3 additions & 0 deletions Core/XMPPFramework.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#import "XMPPTimer.h"
#import "XMPPCoreDataStorage.h"
#import "XMPPCoreDataStorageProtected.h"
#import "XMPPDelayedDelivery.h"
#import "NSXMLElement+XEP_0203.h"
#import "XMPPFileTransfer.h"
#import "XMPPIncomingFileTransfer.h"
Expand Down Expand Up @@ -132,6 +133,7 @@
#import "XMPPStreamManagementMemoryStorage.h"
#import "XMPPStreamManagementStanzas.h"
#import "XMPPStreamManagement.h"
#import "XMPPManagedMessaging.h"
#import "XMPPAutoPing.h"
#import "XMPPPing.h"
#import "XMPPAutoTime.h"
Expand Down Expand Up @@ -164,6 +166,7 @@
#import "XMPPRoomLightCoreDataStorage.h"
#import "XMPPRoomLightCoreDataStorageProtected.h"
#import "XMPPRoomLightMessageCoreDataStorageObject.h"
#import "XMPPOneToOneChat.h"


FOUNDATION_EXPORT double XMPPFrameworkVersionNumber;
Expand Down
27 changes: 27 additions & 0 deletions Extensions/OneToOneChat/XMPPOneToOneChat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#import "XMPPModule.h"

NS_ASSUME_NONNULL_BEGIN

@class XMPPMessage;

/// @brief A module that handles one-to-one chat messaging.
/// @discussion This module triggers delegate callbacks for all sent or received messages of type 'chat'.
@interface XMPPOneToOneChat : XMPPModule

@end

/// A protocol defining @c XMPPOneToOneChat module delegate API.
@protocol XMPPOneToOneChatDelegate <NSObject>

@optional
/// Notifies the delegate that a chat message has been received in the stream.
- (void)xmppOneToOneChat:(XMPPOneToOneChat *)xmppOneToOneChat didReceiveChatMessage:(XMPPMessage *)message
NS_SWIFT_NAME(xmppOneToOneChat(_:didReceiveChatMessage:));

/// Notifies the delegate that a chat message has been sent in the stream.
- (void)xmppOneToOneChat:(XMPPOneToOneChat *)xmppOneToOneChat didSendChatMessage:(XMPPMessage *)message
NS_SWIFT_NAME(xmppOneToOneChat(_:didSendChatMessage:));

@end

NS_ASSUME_NONNULL_END
40 changes: 40 additions & 0 deletions Extensions/OneToOneChat/XMPPOneToOneChat.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#import "XMPPOneToOneChat.h"
#import "XMPPMessage.h"
#import "XMPPStream.h"
#import "XMPPLogging.h"

// Log levels: off, error, warn, info, verbose
// Log flags: trace
#if DEBUG
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN; // | XMPP_LOG_FLAG_TRACE;
#else
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
#endif

@implementation XMPPOneToOneChat

- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
XMPPLogTrace();

if (![message isChatMessage]) {
return;
}

XMPPLogInfo(@"Received chat message from %@", [message from]);
[multicastDelegate xmppOneToOneChat:self didReceiveChatMessage:message];
}

- (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message
{
XMPPLogTrace();

if (![message isChatMessage]) {
return;
}

XMPPLogInfo(@"Sent chat message to %@", [message to]);
[multicastDelegate xmppOneToOneChat:self didSendChatMessage:message];
}

@end
16 changes: 16 additions & 0 deletions Extensions/XEP-0184/XMPPMessageDeliveryReceipts.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#define _XMPP_MESSAGE_DELIVERY_RECEIPTS_H

@class XMPPMessage;

/**
* XMPPMessageDeliveryReceipts can be configured to automatically send delivery receipts and requests in accordance to XEP-0184
**/
Expand Down Expand Up @@ -30,5 +32,19 @@ NS_ASSUME_NONNULL_BEGIN

@property (assign) BOOL autoSendMessageDeliveryReceipts;

@end

/**
* A protocol defining @c XMPPManagedMessaging module delegate API.
**/
@protocol XMPPMessageDeliveryReceiptsDelegate <NSObject>

@optional

/**
* Notifies the delegate of a receipt response message received in the stream.
**/
- (void)xmppMessageDeliveryReceipts:(XMPPMessageDeliveryReceipts *)xmppMessageDeliveryReceipts didReceiveReceiptResponseMessage:(XMPPMessage *)message;

@end
NS_ASSUME_NONNULL_END
5 changes: 5 additions & 0 deletions Extensions/XEP-0184/XMPPMessageDeliveryReceipts.m
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ - (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
[sender sendElement:generatedReceiptResponse];
}
}

if ([message hasReceiptResponse])
{
[multicastDelegate xmppMessageDeliveryReceipts:self didReceiveReceiptResponseMessage:message];
}
}

- (XMPPMessage *)xmppStream:(XMPPStream *)sender willSendMessage:(XMPPMessage *)message
Expand Down
34 changes: 34 additions & 0 deletions Extensions/XEP-0198/Managed Messaging/XMPPManagedMessaging.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#import "XMPPModule.h"

NS_ASSUME_NONNULL_BEGIN

@class XMPPMessage;

/**
A module working in tandem with @c XMPPStreamManagement to trace outgoing message stream acknowledgements.
This module only monitors messages with @c elementID assigned. The rationale behind this is that any potential retransmissions
of messages without IDs will cause deduplication issues on the receiving end.
*/
@interface XMPPManagedMessaging : XMPPModule

@end

/// A protocol defining @c XMPPManagedMessaging module delegate API.
@protocol XMPPManagedMessagingDelegate <NSObject>

@optional

/// Notifies the delegate that a message subject to monitoring has been sent in the stream.
- (void)xmppManagedMessaging:(XMPPManagedMessaging *)sender didBeginMonitoringOutgoingMessage:(XMPPMessage *)message;

/// Notifies the delegate that @c XMPPStreamManagement module has received server acknowledgement for sent messages with given IDs.
- (void)xmppManagedMessaging:(XMPPManagedMessaging *)sender didConfirmSentMessagesWithIDs:(NSArray<NSString *> *)messageIDs;

/// @brief Notifies the delegate that post-reauthentication message acknowledgement processing is finished.
/// At this point, no more acknowledgements for currently monitored messages are to be expected.
- (void)xmppManagedMessagingDidFinishProcessingPreviousStreamConfirmations:(XMPPManagedMessaging *)sender;

@end

NS_ASSUME_NONNULL_END
112 changes: 112 additions & 0 deletions Extensions/XEP-0198/Managed Messaging/XMPPManagedMessaging.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#import "XMPPManagedMessaging.h"
#import "XMPPStreamManagement.h"
#import "XMPPLogging.h"

// Log levels: off, error, warn, info, verbose
// Log flags: trace
#if DEBUG
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN; // | XMPP_LOG_FLAG_TRACE;
#else
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
#endif

static NSString * const XMPPManagedMessagingURLScheme = @"xmppmanagedmessage";

@implementation XMPPManagedMessaging

- (void)didActivate
{
XMPPLogTrace();
[self.xmppStream autoAddDelegate:self delegateQueue:self.moduleQueue toModulesOfClass:[XMPPStreamManagement class]];
}

- (void)willDeactivate
{
XMPPLogTrace();
[self.xmppStream removeAutoDelegate:self delegateQueue:self.moduleQueue fromModulesOfClass:[XMPPStreamManagement class]];
}

- (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message
{
XMPPLogTrace();

if (![message elementID]) {
XMPPLogWarn(@"Sent message without an ID excluded from managed messaging");
return;
}

XMPPLogInfo(@"Registering message with ID=%@ for managed messaging", [message elementID]);
[multicastDelegate xmppManagedMessaging:self didBeginMonitoringOutgoingMessage:message];
}

- (id)xmppStreamManagement:(XMPPStreamManagement *)sender stanzaIdForSentElement:(XMPPElement *)element
{
if (![element isKindOfClass:[XMPPMessage class]] || ![element elementID]) {
return nil;
}

NSURLComponents *managedMessageURLComponents = [[NSURLComponents alloc] init];
managedMessageURLComponents.scheme = XMPPManagedMessagingURLScheme;
managedMessageURLComponents.path = [element elementID];

return managedMessageURLComponents.URL;
}

- (void)xmppStreamManagement:(XMPPStreamManagement *)sender didReceiveAckForStanzaIds:(NSArray *)stanzaIds
{
XMPPLogTrace();

NSArray *resumeStanzaIDs;
[sender didResumeWithAckedStanzaIds:&resumeStanzaIDs serverResponse:nil];
if ([resumeStanzaIDs isEqualToArray:stanzaIds]) {
// Handled in -xmppStreamDidAuthenticate:
return;
}

[self processStreamManagementAcknowledgementForStanzaIDs:stanzaIds];
}

- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
XMPPLogTrace();

dispatch_group_t stanzaAcknowledgementGroup = dispatch_group_create();

[sender enumerateModulesOfClass:[XMPPStreamManagement class] withBlock:^(XMPPModule *module, NSUInteger idx, BOOL *stop) {
NSArray *acknowledgedStanzaIDs;
[(XMPPStreamManagement *)module didResumeWithAckedStanzaIds:&acknowledgedStanzaIDs serverResponse:nil];
if (acknowledgedStanzaIDs.count == 0) {
return;
}

dispatch_group_async(stanzaAcknowledgementGroup, self.moduleQueue, ^{
[self processStreamManagementAcknowledgementForStanzaIDs:acknowledgedStanzaIDs];
});
}];

dispatch_group_notify(stanzaAcknowledgementGroup, self.moduleQueue, ^{
[multicastDelegate xmppManagedMessagingDidFinishProcessingPreviousStreamConfirmations:self];
});
}

- (void)processStreamManagementAcknowledgementForStanzaIDs:(NSArray *)stanzaIDs
{
NSMutableArray *managedMessageIDs = [NSMutableArray array];
for (id stanzaID in stanzaIDs) {
if (![stanzaID isKindOfClass:[NSURL class]] || ![((NSURL *)stanzaID).scheme isEqualToString:XMPPManagedMessagingURLScheme]) {
continue;
}
// Extracting path directly from NSURL does not work if it doesn't start with "/"
NSURLComponents *managedMessageURLComponents = [[NSURLComponents alloc] initWithURL:stanzaID resolvingAgainstBaseURL:NO];
[managedMessageIDs addObject:managedMessageURLComponents.path];
}

if (managedMessageIDs.count == 0) {
return;
}

XMPPLogInfo(@"Confirming managed messages with IDs={%@}", [managedMessageIDs componentsJoinedByString:@","]);
[multicastDelegate xmppManagedMessaging:self didConfirmSentMessagesWithIDs:managedMessageIDs];
}

@end
3 changes: 3 additions & 0 deletions Extensions/XEP-0203/NSXMLElement+XEP_0203.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
#import <Foundation/Foundation.h>
@import KissXML;

@class XMPPJID;

@interface NSXMLElement (XEP_0203)

@property (nonatomic, readonly) BOOL wasDelayed;
@property (nonatomic, readonly, nullable) NSDate *delayedDeliveryDate;
@property (nonatomic, readonly, nullable) XMPPJID *delayedDeliveryFrom;
@property (nonatomic, readonly, nullable) NSString *delayedDeliveryReasonDescription;

@end
53 changes: 32 additions & 21 deletions Extensions/XEP-0203/NSXMLElement+XEP_0203.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#import "NSXMLElement+XEP_0203.h"
#import "XMPPDateTimeProfiles.h"
#import "NSXMLElement+XMPP.h"
#import "XMPPJID.h"

#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
Expand All @@ -10,27 +11,11 @@ @implementation NSXMLElement (XEP_0203)

- (BOOL)wasDelayed
{
NSXMLElement *delay;

delay = [self elementForName:@"delay" xmlns:@"urn:xmpp:delay"];
if (delay)
{
return YES;
}

delay = [self elementForName:@"x" xmlns:@"jabber:x:delay"];
if (delay)
{
return YES;
}

return NO;
return [self anyDelayedDeliveryChildElement] != nil;
}

- (NSDate *)delayedDeliveryDate
{
NSXMLElement *delay;

// From XEP-0203 (Delayed Delivery)
//
// <delay xmlns='urn:xmpp:delay'
Expand All @@ -40,7 +25,7 @@ - (NSDate *)delayedDeliveryDate
// The format [of the stamp attribute] MUST adhere to the dateTime format
// specified in XEP-0082 and MUST be expressed in UTC.

delay = [self elementForName:@"delay" xmlns:@"urn:xmpp:delay"];
NSXMLElement *delay = [self delayedDeliveryChildElement];
if (delay)
{
NSString *stampValue = [delay attributeStringValueForName:@"stamp"];
Expand All @@ -60,12 +45,12 @@ - (NSDate *)delayedDeliveryDate
// from='capulet.com'
// stamp='20020910T23:08:25'>

delay = [self elementForName:@"x" xmlns:@"jabber:x:delay"];
if (delay)
NSXMLElement *legacyDelay = [self legacyDelayedDeliveryChildElement];
if (legacyDelay)
{
NSDate *stamp;

NSString *stampValue = [delay attributeStringValueForName:@"stamp"];
NSString *stampValue = [legacyDelay attributeStringValueForName:@"stamp"];

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
Expand All @@ -81,4 +66,30 @@ - (NSDate *)delayedDeliveryDate
return nil;
}

- (XMPPJID *)delayedDeliveryFrom
{
NSString *delayedDeliveryFromString = [[self anyDelayedDeliveryChildElement] attributeStringValueForName:@"from"];
return delayedDeliveryFromString ? [XMPPJID jidWithString:delayedDeliveryFromString] : nil;
}

- (NSString *)delayedDeliveryReasonDescription
{
return [self anyDelayedDeliveryChildElement].stringValue;
}

- (NSXMLElement *)delayedDeliveryChildElement
{
return [self elementForName:@"delay" xmlns:@"urn:xmpp:delay"];
}

- (NSXMLElement *)legacyDelayedDeliveryChildElement
{
return [self elementForName:@"x" xmlns:@"jabber:x:delay"];
}

- (NSXMLElement *)anyDelayedDeliveryChildElement
{
return [self delayedDeliveryChildElement] ?: [self legacyDelayedDeliveryChildElement];
}

@end
Loading

0 comments on commit 60fa06c

Please sign in to comment.