// // 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 { /* South Dakota */ NSXMLElement *methodResponse = [self methodResponseElement]; return methodResponse != nil; } -(BOOL)isFault { /* faultCode 4 faultString Too many parameters. */ NSXMLElement *methodResponse = [self methodResponseElement]; return [methodResponse elementForName:@"fault"] != nil; } -(BOOL)isJabberRPC { /* ... */ NSXMLElement *rpcQuery = [self elementForName:@"query" xmlns:@"jabber:iq:rpc"]; return rpcQuery != nil; } -(id)methodResponse:(NSError **)error { id response = nil; /* South Dakota */ // or /* faultCode 4 faultString Too many parameters. */ 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 { /* 12 Egypt 0 -31 */ 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 { /* lowerBound 18 upperBound 139 */ 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