2016-02-24 16:56:39 +01:00

223 lines
5.2 KiB
Objective-C

//
// XMPPLastActivity.m
// XEP-0012
//
// Created by Daniel Rodríguez Troitiño on 1/26/2013.
//
//
#import "XMPPLastActivity.h"
#import "XMPPIDTracker.h"
#import "XMPPIQ+LastActivity.h"
#import "XMPPFramework.h"
#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
static const NSTimeInterval XMPPLastActivityDefaultTimeout = 30.0;
@interface XMPPLastActivity ()
@property (atomic, strong) XMPPIDTracker *queryTracker;
@end
@implementation XMPPLastActivity
@synthesize respondsToQueries = _respondsToQueries;
@synthesize queryTracker = _queryTracker;
- (id)init
{
return [self initWithDispatchQueue:NULL];
}
- (id)initWithDispatchQueue:(dispatch_queue_t)queue
{
if ((self = [super initWithDispatchQueue:queue]))
{
_respondsToQueries = YES;
}
return self;
}
- (BOOL)activate:(XMPPStream *)aXmppStream
{
if ([super activate:aXmppStream])
{
#ifdef _XMPP_CAPABILITIES_H
[xmppStream autoAddDelegate:self delegateQueue:moduleQueue toModulesOfClass:[XMPPCapabilities class]];
#endif
_queryTracker = [[XMPPIDTracker alloc] initWithDispatchQueue:moduleQueue];
return YES;
}
return NO;
}
- (void)deactivate
{
#ifdef _XMPP_CAPABILITIES_H
[xmppStream removeAutoDelegate:self delegateQueue:moduleQueue fromModulesOfClass:[XMPPCapabilities class]];
#endif
dispatch_block_t block = ^{ @autoreleasepool {
[_queryTracker removeAllIDs];
_queryTracker = nil;
}};
if (dispatch_get_specific(moduleQueueTag))
block();
else
dispatch_sync(moduleQueue, block);
[super deactivate];
}
- (BOOL)respondsToQueries
{
if (dispatch_get_specific(moduleQueueTag))
{
return _respondsToQueries;
}
else
{
__block BOOL result;
dispatch_sync(moduleQueue, ^{
result = _respondsToQueries;
});
return result;
}
}
- (void)setRespondsToQueries:(BOOL)respondsToQueries
{
dispatch_block_t block = ^{
if (_respondsToQueries != respondsToQueries)
{
_respondsToQueries = respondsToQueries;
#ifdef _XMPP_CAPABILITIES_H
@autoreleasepool {
// Capabilities may have changed, need to notify others
[xmppStream resendMyPresence];
}
#endif
}
};
if (dispatch_get_specific(moduleQueueTag))
block();
else
dispatch_sync(moduleQueue, block);
}
- (NSString *)sendLastActivityQueryToJID:(XMPPJID *)jid
{
return [self sendLastActivityQueryToJID:jid withTimeout:XMPPLastActivityDefaultTimeout];
}
- (NSString *)sendLastActivityQueryToJID:(XMPPJID *)jid withTimeout:(NSTimeInterval)timeout
{
XMPPIQ *query = [XMPPIQ lastActivityQueryTo:jid];
NSString *queryID = query.elementID;
dispatch_async(moduleQueue, ^{
__weak __typeof__(self) self_weak_ = self;
[_queryTracker addID:queryID block:^(XMPPIQ *iq, id<XMPPTrackingInfo> info) {
__strong __typeof__(self) self = self_weak_;
if (iq)
{
[self delegateDidReceiveResponse:iq];
}
else
{
[self delegateDidNotReceiveResponse:info.elementID dueToTimeout:info.timeout];
}
} timeout:timeout];
[xmppStream sendElement:query];
});
return queryID;
}
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
{
NSString *type = [iq type];
if (([type isEqualToString:@"result"] || [type isEqualToString:@"error"]))
{
return [_queryTracker invokeForID:iq.elementID withObject:iq];
}
else if (_respondsToQueries && [type isEqualToString:@"get"] && [iq isLastActivityQuery])
{
NSUInteger seconds = [self delegateNumberOfIdleTimeSecondsWithIQ:iq];
XMPPIQ *response = [XMPPIQ lastActivityResponseToIQ:iq withSeconds:seconds];
[sender sendElement:response];
return YES;
}
return NO;
}
- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
{
[_queryTracker removeAllIDs];
}
#ifdef _XMPP_CAPABILITIES_H
// If an XMPPCapabilities instance is used we want to advertise our support for last activity.
- (void)xmppCapabilities:(XMPPCapabilities *)sender collectingMyCapabilities:(NSXMLElement *)query
{
if (_respondsToQueries)
{
NSXMLElement *feature = [NSXMLElement elementWithName:@"feature"];
[feature addAttributeWithName:@"var" stringValue:XMPPLastActivityNamespace];
[query addChild:feature];
}
}
#endif
- (void)delegateDidReceiveResponse:(XMPPIQ *)response
{
[multicastDelegate xmppLastActivity:self didReceiveResponse:response];
}
- (void)delegateDidNotReceiveResponse:(NSString *)queryID dueToTimeout:(NSTimeInterval)timeout
{
[multicastDelegate xmppLastActivity:self didNotReceiveResponse:queryID dueToTimeout:timeout];
}
- (NSUInteger)delegateNumberOfIdleTimeSecondsWithIQ:(XMPPIQ *)iq
{
__block NSUInteger idleSeconds = NSNotFound;
GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
id delegate;
dispatch_queue_t delegateQueue;
SEL selector = @selector(numberOfIdleTimeSecondsForXMPPLastActivity:queryIQ:currentIdleTimeSeconds:);
while ([delegateEnumerator getNextDelegate:&delegate delegateQueue:&delegateQueue forSelector:selector])
{
dispatch_sync(delegateQueue, ^{ @autoreleasepool {
idleSeconds = [delegate numberOfIdleTimeSecondsForXMPPLastActivity:self queryIQ:iq currentIdleTimeSeconds:idleSeconds];
}});
}
return idleSeconds == NSNotFound ? 0 : idleSeconds;
}
@end