This commit is contained in:
giuseppenuc committed 2016-02-24 16:56:39 +01:00
1 parent d281d765ea
commit 5b074a5176
1261 files changed
+199158 -7303

No files matched your search

+77
View File
@@ -0,0 +1,77 @@
//
// XMPPIQ+JabberRPC.h
// XEP-0009
//
// Created by Eric Chamberlain on 5/16/10.
//
#import <Foundation/Foundation.h>
#import "XMPPIQ.h"
@interface XMPPIQ(JabberRPC)
/**
* Creates and returns a new autoreleased XMPPIQ.
* This is the only method you normally need to call.
**/
+ (XMPPIQ *)rpcTo:(XMPPJID *)jid methodName:(NSString *)method parameters:(NSArray *)parameters;
#pragma mark -
#pragma mark Element helper methods
// returns a Jabber-RPC query elelement
// <query xmlns='jabber:iq:rpc'>
+(NSXMLElement *)elementRpcQuery;
// returns a Jabber-RPC methodCall element
// <methodCall>
+(NSXMLElement *)elementMethodCall;
// returns a Jabber-RPC methodName element
// <methodName>method</methodName>
+(NSXMLElement *)elementMethodName:(NSString *)method;
// returns a Jabber-RPC params element
// <params>
+(NSXMLElement *)elementParams;
#pragma mark -
#pragma mark Disco elements
// returns the Disco query identity element
// <identity category='automation' type='rpc'/>
+(NSXMLElement *)elementRpcIdentity;
// returns the Disco query feature element
// <feature var='jabber:iq:rpc'/>
+(NSXMLElement *)elementRpcFeature;
#pragma mark -
#pragma mark Conversion methods
// encode any object into JabberRPC formatted element
// this method calls the others
+(NSXMLElement *)paramElementFromObject:(id)object;
+(NSXMLElement *)valueElementFromObject:(id)object;
+(NSXMLElement *)valueElementFromArray:(NSArray *)array;
+(NSXMLElement *)valueElementFromDictionary:(NSDictionary *)dictionary;
+(NSXMLElement *)valueElementFromBoolean:(CFBooleanRef)boolean;
+(NSXMLElement *)valueElementFromNumber:(NSNumber *)number;
+(NSXMLElement *)valueElementFromString:(NSString *)string;
+(NSXMLElement *)valueElementFromDate:(NSDate *)date;
+(NSXMLElement *)valueElementFromData:(NSData *)data;
+(NSXMLElement *)valueElementFromElementWithName:(NSString *)elementName value:(NSString *)value;
#pragma mark Wrapper methods
+(NSXMLElement *)wrapElement:(NSString *)elementName aroundElement:(NSXMLElement *)element;
+(NSXMLElement *)wrapValueElementAroundElement:(NSXMLElement *)element;
@end
+219
View File
@@ -0,0 +1,219 @@
//
// XMPPIQ+JabberRPC.m
// XEP-0009
//
// Created by Eric Chamberlain on 5/16/10.
//
#import "XMPPIQ+JabberRPC.h"
#import "XMPP.h"
#import "NSData+XMPP.h"
#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
@implementation XMPPIQ(JabberRPC)
+(XMPPIQ *)rpcTo:(XMPPJID *)jid methodName:(NSString *)method parameters:(NSArray *)parameters {
// Send JabberRPC element
//
// <iq to="fullJID" type="set" id="elementID">
// <query xmlns='jabber:iq:rpc'>
// <methodCall>
// <methodName>method</methodName>
// <params>
// <param>
// <value><string>example</string></value>
// </param>
// ...
// </params>
// </methodCall>
// </query>
// </iq>
XMPPIQ *iq = [XMPPIQ iqWithType:@"set" to:jid elementID:[XMPPStream generateUUID]];
NSXMLElement *jabberRPC = [self elementRpcQuery];
NSXMLElement *methodCall = [self elementMethodCall];
NSXMLElement *methodName = [self elementMethodName:method];
NSXMLElement *params = [self elementParams];
for (id parameter in parameters) {
[params addChild:[self paramElementFromObject:parameter]];
}
[methodCall addChild:methodName];
[methodCall addChild:params];
[jabberRPC addChild:methodCall];
[iq addChild:jabberRPC];
return iq;
}
#pragma mark Element helper methods
+(NSXMLElement *)elementRpcQuery {
return [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:rpc"];
}
+(NSXMLElement *)elementMethodCall {
return [NSXMLElement elementWithName:@"methodCall"];
}
+(NSXMLElement *)elementMethodName:(NSString *)method {
return [NSXMLElement elementWithName:@"methodName" stringValue:method];
}
+(NSXMLElement *)elementParams {
return [NSXMLElement elementWithName:@"params"];
}
#pragma mark -
#pragma mark Disco elements
+(NSXMLElement *)elementRpcIdentity {
NSXMLElement *identity = [NSXMLElement elementWithName:@"identity"];
[identity addAttributeWithName:@"category" stringValue:@"automation"];
[identity addAttributeWithName:@"type" stringValue:@"rpc"];
return identity;
}
// returns the Disco query feature element
// <feature var='jabber:iq:rpc'/>
+(NSXMLElement *)elementRpcFeature {
NSXMLElement *feature = [NSXMLElement elementWithName:@"feature"];
[feature addAttributeWithName:@"var" stringValue:@"jabber:iq:rpc"];
return feature;
}
#pragma mark Conversion methods
+(NSXMLElement *)paramElementFromObject:(id)object {
if (!object) {
return nil;
}
return [self wrapElement:@"param" aroundElement:[self valueElementFromObject:object]];
}
+(NSXMLElement *)valueElementFromObject:(id)object {
if (!object) {
return nil;
}
if ([object isKindOfClass: [NSArray class]]) {
return [self valueElementFromArray: object];
} else if ([object isKindOfClass: [NSDictionary class]]) {
return [self valueElementFromDictionary: object];
} else if (((__bridge CFBooleanRef)object == kCFBooleanTrue) || ((__bridge CFBooleanRef)object == kCFBooleanFalse)) {
return [self valueElementFromBoolean: (__bridge CFBooleanRef)object];
} else if ([object isKindOfClass: [NSNumber class]]) {
return [self valueElementFromNumber: object];
} else if ([object isKindOfClass: [NSString class]]) {
return [self valueElementFromString: object];
} else if ([object isKindOfClass: [NSDate class]]) {
return [self valueElementFromDate: object];
} else if ([object isKindOfClass: [NSData class]]) {
return [self valueElementFromData: object];
} else {
return [self valueElementFromString: object];
}
}
+(NSXMLElement *)valueElementFromArray:(NSArray *)array {
NSXMLElement *data = [NSXMLElement elementWithName:@"data"];
for (id object in array) {
[data addChild:[self valueElementFromObject:object]];
}
return [self wrapValueElementAroundElement:data];
}
+(NSXMLElement *)valueElementFromDictionary:(NSDictionary *)dictionary {
NSXMLElement *structElement = [NSXMLElement elementWithName:@"struct"];
NSXMLElement *member;
NSXMLElement *name;
for (NSString *key in dictionary) {
member = [NSXMLElement elementWithName:@"member"];
name = [NSXMLElement elementWithName:@"name" stringValue:key];
[member addChild:name];
[member addChild:[self valueElementFromObject:dictionary[key]]];
}
return [self wrapValueElementAroundElement:structElement];
}
+(NSXMLElement *)valueElementFromBoolean:(CFBooleanRef)boolean {
if (boolean == kCFBooleanTrue) {
return [self valueElementFromElementWithName:@"boolean" value:@"1"];
} else {
return [self valueElementFromElementWithName:@"boolean" value:@"0"];
}
}
+(NSXMLElement *)valueElementFromNumber:(NSNumber *)number {
// what type of NSNumber is this?
if ([[NSString stringWithCString: [number objCType]
encoding: NSUTF8StringEncoding] isEqualToString: @"d"]) {
return [self valueElementFromElementWithName:@"double" value:[number stringValue]];
} else {
return [self valueElementFromElementWithName:@"i4" value:[number stringValue]];
}
}
+(NSXMLElement *)valueElementFromString:(NSString *)string {
return [self valueElementFromElementWithName:@"string" value:string];
}
+(NSXMLElement *)valueElementFromDate:(NSDate *)date {
unsigned calendarComponents = kCFCalendarUnitYear |
kCFCalendarUnitMonth |
kCFCalendarUnitDay |
kCFCalendarUnitHour |
kCFCalendarUnitMinute |
kCFCalendarUnitSecond;
NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:calendarComponents fromDate:date];
NSString *dateString = [NSString stringWithFormat: @"%.4ld%.2ld%.2ldT%.2ld:%.2ld:%.2ld",
(long)[dateComponents year],
(long)[dateComponents month],
(long)[dateComponents day],
(long)[dateComponents hour],
(long)[dateComponents minute],
(long)[dateComponents second], nil];
return [self valueElementFromElementWithName:@"dateTime.iso8601" value: dateString];
}
+(NSXMLElement *)valueElementFromData:(NSData *)data {
return [self valueElementFromElementWithName:@"base64" value:[data xmpp_base64Encoded]];
}
+(NSXMLElement *)valueElementFromElementWithName:(NSString *)elementName value:(NSString *)value {
return [self wrapValueElementAroundElement:[NSXMLElement elementWithName:elementName stringValue:value]];
}
#pragma mark Wrapper methods
+(NSXMLElement *)wrapElement:(NSString *)elementName aroundElement:(NSXMLElement *)element {
NSXMLElement *wrapper = [NSXMLElement elementWithName:elementName];
[wrapper addChild:element];
return wrapper;
}
+(NSXMLElement *)wrapValueElementAroundElement:(NSXMLElement *)element {
return [self wrapElement:@"value" aroundElement:element];
}
@end
@@ -0,0 +1,66 @@
//
// XMPPIQ+JabberRPCResonse.h
// XEP-0009
//
// Created by Eric Chamberlain on 5/25/10.
//
#import "XMPPIQ.h"
typedef enum {
JabberRPCElementTypeArray,
JabberRPCElementTypeDictionary,
JabberRPCElementTypeMember,
JabberRPCElementTypeName,
JabberRPCElementTypeInteger,
JabberRPCElementTypeDouble,
JabberRPCElementTypeBoolean,
JabberRPCElementTypeString,
JabberRPCElementTypeDate,
JabberRPCElementTypeData
} JabberRPCElementType;
@interface XMPPIQ(JabberRPCResonse)
-(NSXMLElement *)methodResponseElement;
// is this a Jabber RPC method response
-(BOOL)isMethodResponse;
-(BOOL)isFault;
-(BOOL)isJabberRPC;
-(id)methodResponse:(NSError **)error;
-(id)objectFromElement:(NSXMLElement *)param;
#pragma mark -
-(NSArray *)parseArray:(NSXMLElement *)arrayElement;
-(NSDictionary *)parseStruct:(NSXMLElement *)structElement;
-(NSDictionary *)parseMember:(NSXMLElement *)memberElement;
#pragma mark -
- (NSDate *)parseDateString: (NSString *)dateString withFormat: (NSString *)format;
#pragma mark -
- (NSNumber *)parseInteger: (NSString *)value;
- (NSNumber *)parseDouble: (NSString *)value;
- (NSNumber *)parseBoolean: (NSString *)value;
- (NSString *)parseString: (NSString *)value;
- (NSDate *)parseDate: (NSString *)value;
- (NSData *)parseData: (NSString *)value;
@end
@@ -0,0 +1,290 @@
//
// XMPPIQ+JabberRPCResonse.m
// XEP-0009
//
// Created by Eric Chamberlain on 5/25/10.
//
#import "XMPPIQ+JabberRPCResonse.h"
#import "XMPPJabberRPCModule.h"
#import "XMPPLogging.h"
#import "NSData+XMPP.h"
#import "NSXMLElement+XMPP.h"
#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
// Log levels: off, error, warn, info, verbose
#if DEBUG
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
#else
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
#endif
@implementation XMPPIQ(JabberRPCResonse)
-(NSXMLElement *)methodResponseElement {
NSXMLElement *query = [self elementForName:@"query"];
return [query elementForName:@"methodResponse"];
}
// is this a Jabber RPC method response
-(BOOL)isMethodResponse {
/*
<methodResponse>
<params>
<param>
<value><string>South Dakota</string></value>
</param>
</params>
</methodResponse>
*/
NSXMLElement *methodResponse = [self methodResponseElement];
return methodResponse != nil;
}
-(BOOL)isFault {
/*
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>4</int></value>
</member>
<member>
<name>faultString</name>
<value><string>Too many parameters.</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>
*/
NSXMLElement *methodResponse = [self methodResponseElement];
return [methodResponse elementForName:@"fault"] != nil;
}
-(BOOL)isJabberRPC {
/*
<query xmlns="jabber:iq:rpc">
...
</query>
*/
NSXMLElement *rpcQuery = [self elementForName:@"query" xmlns:@"jabber:iq:rpc"];
return rpcQuery != nil;
}
-(id)methodResponse:(NSError **)error {
id response = nil;
/*
<methodResponse>
<params>
<param>
<value><string>South Dakota</string></value>
</param>
</params>
</methodResponse>
*/
// or
/*
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>4</int></value>
</member>
<member>
<name>faultString</name>
<value><string>Too many parameters.</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>
*/
NSXMLElement *methodResponse = [self methodResponseElement];
// parse the methodResponse
response = [self objectFromElement:(NSXMLElement *)[methodResponse childAtIndex:0]];
if ([self isFault]) {
// we should produce an error
// response should be a dict
if (error) {
*error = [NSError errorWithDomain:XMPPJabberRPCErrorDomain
code:[[response objectForKey:@"faultCode"] intValue]
userInfo:(NSDictionary *)response];
}
response = nil;
} else {
if (error) {
*error = nil;
}
}
return response;
}
-(id)objectFromElement:(NSXMLElement *)param {
NSString *element = [param name];
if ([element isEqualToString:@"params"] ||
[element isEqualToString:@"param"] ||
[element isEqualToString:@"fault"] ||
[element isEqualToString:@"value"]) {
NSXMLElement *firstChild = (NSXMLElement *)[param childAtIndex:0];
if (firstChild) {
return [self objectFromElement:firstChild];
} else {
// no child element, treat it like a string
return [self parseString:[param stringValue]];
}
} else if ([element isEqualToString:@"string"] ||
[element isEqualToString:@"name"]) {
return [self parseString:[param stringValue]];
} else if ([element isEqualToString:@"member"]) {
return [self parseMember:param];
} else if ([element isEqualToString:@"array"]) {
return [self parseArray:param];
} else if ([element isEqualToString:@"struct"]) {
return [self parseStruct:param];
} else if ([element isEqualToString:@"int"]) {
return [self parseInteger:[param stringValue]];
} else if ([element isEqualToString:@"double"]) {
return [self parseDouble:[param stringValue]];
} else if ([element isEqualToString:@"boolean"]) {
return [self parseBoolean:[param stringValue]];
} else if ([element isEqualToString:@"dateTime.iso8601"]) {
return [self parseDate:[param stringValue]];
} else if ([element isEqualToString:@"base64"]) {
return [self parseData:[param stringValue]];
} else {
// bad element
XMPPLogWarn(@"%@: %@ - bad element: %@", THIS_FILE, THIS_METHOD, [param stringValue]);
}
return nil;
}
#pragma mark -
-(NSArray *)parseArray:(NSXMLElement *)arrayElement {
/*
<array>
<data>
<value><i4>12</i4></value>
<value><string>Egypt</string></value>
<value><boolean>0</boolean></value>
<value><i4>-31</i4></value>
</data>
</array>
*/
NSXMLElement *data = (NSXMLElement *)[arrayElement childAtIndex:0];
NSArray *children = [data children];
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[children count]];
for (NSXMLElement *child in children) {
[array addObject:[self objectFromElement:child]];
}
return array;
}
-(NSDictionary *)parseStruct:(NSXMLElement *)structElement {
/*
<struct>
<member>
<name>lowerBound</name>
<value><i4>18</i4></value>
</member>
<member>
<name>upperBound</name>
<value><i4>139</i4></value>
</member>
</struct>
*/
NSArray *children = [structElement children];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:[children count]];
for (NSXMLElement *child in children) {
[dict addEntriesFromDictionary:[self parseMember:child]];
}
return dict;
}
-(NSDictionary *)parseMember:(NSXMLElement *)memberElement {
NSString *key = [self objectFromElement:[memberElement elementForName:@"name"]];
id value = [self objectFromElement:[memberElement elementForName:@"value"]];
return @{key : value};
}
#pragma mark -
- (NSDate *)parseDateString: (NSString *)dateString withFormat: (NSString *)format {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
[dateFormatter setDateFormat: format];
NSDate *result = [dateFormatter dateFromString: dateString];
return result;
}
#pragma mark -
- (NSNumber *)parseInteger: (NSString *)value {
return @([value integerValue]);
}
- (NSNumber *)parseDouble: (NSString *)value {
return @([value doubleValue]);
}
- (NSNumber *)parseBoolean: (NSString *)value {
if ([value isEqualToString: @"1"]) {
return @YES;
}
return @NO;
}
- (NSString *)parseString: (NSString *)value {
return [value stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
- (NSDate *)parseDate: (NSString *)value {
NSDate *result = nil;
result = [self parseDateString: value withFormat: @"yyyyMMdd'T'HH:mm:ss"];
if (!result) {
result = [self parseDateString: value withFormat: @"yyyy'-'MM'-'dd'T'HH:mm:ss"];
}
return result;
}
- (NSData *)parseData: (NSString *)value {
// Convert the base 64 encoded data into a string
NSData *base64Data = [value dataUsingEncoding:NSASCIIStringEncoding];
NSData *decodedData = [base64Data xmpp_base64Decoded];
return decodedData;
}
@end
+50
View File
@@ -0,0 +1,50 @@
//
// XMPPJabberRPCModule.h
// XEP-0009
//
// Originally created by Eric Chamberlain on 5/16/10.
//
#import <Foundation/Foundation.h>
#import "XMPPModule.h"
#define _XMPP_JABBER_RPC_MODULE_H
extern NSString *const XMPPJabberRPCErrorDomain;
@class XMPPJID;
@class XMPPStream;
@class XMPPIQ;
@protocol XMPPJabberRPCModuleDelegate;
@interface XMPPJabberRPCModule : XMPPModule
{
NSMutableDictionary *rpcIDs;
NSTimeInterval defaultTimeout;
}
@property (nonatomic, assign) NSTimeInterval defaultTimeout;
- (NSString *)sendRpcIQ:(XMPPIQ *)iq;
- (NSString *)sendRpcIQ:(XMPPIQ *)iq withTimeout:(NSTimeInterval)timeout;
// caller knows best when a request has timed out
// it should remove the rpcID, on timeout.
- (void)timeoutRemoveRpcID:(NSString *)rpcID;
@end
@protocol XMPPJabberRPCModuleDelegate
@optional
// sent when transport error is received
-(void)jabberRPC:(XMPPJabberRPCModule *)sender elementID:(NSString *)elementID didReceiveError:(NSError *)error;
// sent when a methodResponse comes back
-(void)jabberRPC:(XMPPJabberRPCModule *)sender elementID:(NSString *)elementID didReceiveMethodResponse:(id)response;
// sent when a Jabber-RPC server request is received
-(void)jabberRPC:(XMPPJabberRPCModule *)sender didReceiveSetIQ:(XMPPIQ *)iq;
@end
+341
View File
@@ -0,0 +1,341 @@
//
// XMPPJabberRPCModule.m
// XEP-0009
//
// Originally created by Eric Chamberlain on 5/16/10.
//
#import "XMPPJabberRPCModule.h"
#import "XMPP.h"
#import "XMPPIQ+JabberRPC.h"
#import "XMPPIQ+JabberRPCResonse.h"
#import "XMPPLogging.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
// 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
NSString *const XMPPJabberRPCErrorDomain = @"XMPPJabberRPCErrorDomain";
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@interface RPCID : NSObject
{
NSString *rpcID;
dispatch_source_t timer;
}
@property (nonatomic, readonly) NSString *rpcID;
@property (nonatomic, readonly) dispatch_source_t timer;
- (id)initWithRpcID:(NSString *)rpcID timer:(dispatch_source_t)timer;
- (void)cancelTimer;
@end
@implementation RPCID
@synthesize rpcID;
@synthesize timer;
- (id)initWithRpcID:(NSString *)aRpcID timer:(dispatch_source_t)aTimer
{
if ((self = [super init]))
{
rpcID = [aRpcID copy];
timer = aTimer;
#if !OS_OBJECT_USE_OBJC
dispatch_retain(timer);
#endif
}
return self;
}
- (BOOL)isEqual:(id)anObject
{
return [rpcID isEqual:anObject];
}
- (NSUInteger)hash
{
return [rpcID hash];
}
- (void)cancelTimer
{
if (timer)
{
dispatch_source_cancel(timer);
#if !OS_OBJECT_USE_OBJC
dispatch_release(timer);
#endif
timer = NULL;
}
}
- (void)dealloc
{
[self cancelTimer];
}
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@implementation XMPPJabberRPCModule
@dynamic defaultTimeout;
- (NSTimeInterval)defaultTimeout
{
__block NSTimeInterval result;
dispatch_block_t block = ^{
result = defaultTimeout;
};
if (dispatch_get_specific(moduleQueueTag))
block();
else
dispatch_sync(moduleQueue, block);
return result;
}
- (void)setDefaultTimeout:(NSTimeInterval)newDefaultTimeout
{
dispatch_block_t block = ^{
XMPPLogTrace();
defaultTimeout = newDefaultTimeout;
};
if (dispatch_get_specific(moduleQueueTag))
block();
else
dispatch_async(moduleQueue, block);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Module Management
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (id)init
{
return [self initWithDispatchQueue:NULL];
}
- (id)initWithDispatchQueue:(dispatch_queue_t)queue
{
if ((self = [super initWithDispatchQueue:queue]))
{
XMPPLogTrace();
rpcIDs = [[NSMutableDictionary alloc] initWithCapacity:5];
defaultTimeout = 5.0;
}
return self;
}
- (BOOL)activate:(XMPPStream *)aXmppStream
{
XMPPLogTrace();
if ([super activate:aXmppStream])
{
#ifdef _XMPP_CAPABILITIES_H
[xmppStream autoAddDelegate:self delegateQueue:moduleQueue toModulesOfClass:[XMPPCapabilities class]];
#endif
return YES;
}
return NO;
}
- (void)deactivate
{
XMPPLogTrace();
#ifdef _XMPP_CAPABILITIES_H
[xmppStream removeAutoDelegate:self delegateQueue:moduleQueue fromModulesOfClass:[XMPPCapabilities class]];
#endif
[super deactivate];
}
- (void)dealloc
{
XMPPLogTrace();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Send RPC
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (NSString *)sendRpcIQ:(XMPPIQ *)iq
{
return [self sendRpcIQ:iq withTimeout:[self defaultTimeout]];
}
- (NSString *)sendRpcIQ:(XMPPIQ *)iq withTimeout:(NSTimeInterval)timeout
{
XMPPLogTrace();
NSString *elementID = [iq elementID];
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, moduleQueue);
dispatch_source_set_event_handler(timer, ^{ @autoreleasepool {
[self timeoutRemoveRpcID:elementID];
}});
dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (timeout * NSEC_PER_SEC));
dispatch_source_set_timer(timer, tt, DISPATCH_TIME_FOREVER, 0);
dispatch_resume(timer);
RPCID *rpcID = [[RPCID alloc] initWithRpcID:elementID timer:timer];
rpcIDs[elementID] = rpcID;
[xmppStream sendElement:iq];
return elementID;
}
- (void)timeoutRemoveRpcID:(NSString *)elementID
{
XMPPLogTrace();
RPCID *rpcID = rpcIDs[elementID];
if (rpcID)
{
[rpcID cancelTimer];
[rpcIDs removeObjectForKey:elementID];
NSError *error = [NSError errorWithDomain:XMPPJabberRPCErrorDomain
code:1400
userInfo:@{@"error" : @"Request timed out"}];
[multicastDelegate jabberRPC:self elementID:elementID didReceiveError:error];
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark XMPPStream delegate
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
{
XMPPLogTrace();
if ([iq isJabberRPC])
{
if ([iq isResultIQ] || [iq isErrorIQ])
{
// Example:
//
// <iq from="deusty.com" to="robbiehanson@deusty.com/work" id="abc123" type="result"/>
NSString *elementID = [iq elementID];
// check if this is a JabberRPC query
// we could check the query element, but we should be able to do a lookup based on the unique elementID
// because we send an ID, we should get one back
RPCID *rpcID = rpcIDs[elementID];
if (rpcID == nil)
{
return NO;
}
XMPPLogVerbose(@"%@: Received RPC response!", THIS_FILE);
if ([iq isResultIQ])
{
id response;
NSError *error = nil;
//TODO: parse iq and generate response.
response = [iq methodResponse:&error];
if (error == nil) {
[multicastDelegate jabberRPC:self elementID:elementID didReceiveMethodResponse:response];
} else {
[multicastDelegate jabberRPC:self elementID:elementID didReceiveError:error];
}
}
else
{
// TODO: implement error parsing
// not much specified in XEP, only 403 forbidden error
NSXMLElement *errorElement = [iq childErrorElement];
NSError *error = [NSError errorWithDomain:XMPPJabberRPCErrorDomain
code:[errorElement attributeIntValueForName:@"code"]
userInfo:@{
@"error" : [errorElement attributesAsDictionary],
@"condition" : [[errorElement childAtIndex:0] name],
@"iq" : iq}];
[multicastDelegate jabberRPC:self elementID:elementID didReceiveError:error];
}
[rpcID cancelTimer];
[rpcIDs removeObjectForKey:elementID];
#ifdef _XMPP_CAPABILITIES_H
} else if ([iq isSetIQ]) {
// we would receive set when implementing Jabber-RPC server
[multicastDelegate jabberRPC:self didReceiveSetIQ:iq];
#endif
}
// Jabber-RPC doesn't use get iq type
}
return NO;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark XMPPCapabilities delegate
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef _XMPP_CAPABILITIES_H
/**
* If an XMPPCapabilites instance is used we want to advertise our support for JabberRPC.
**/
- (void)xmppCapabilities:(XMPPCapabilities *)sender collectingMyCapabilities:(NSXMLElement *)query
{
XMPPLogTrace();
// <query xmlns="http://jabber.org/protocol/disco#info">
// ...
// <identity category='automation' type='rpc'/>
// <feature var='jabber:iq:rpc'/>
// ...
// </query>
[query addChild:[XMPPIQ elementRpcIdentity]];
[query addChild:[XMPPIQ elementRpcFeature]];
}
#endif
@end