- Update
This commit is contained in:
1 parent
d281d765ea
commit
5b074a5176
1261 files changed
+199158
-7303
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in new issue
Block a user