/* Copyright (C) 2000-2005 SKYRIX Software AG This file is part of SOPE. SOPE is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. SOPE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with SOPE; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "XmlRpcCoder.h" #include "XmlRpcValue.h" #include "XmlRpcSaxHandler.h" #include #include "XmlRpcMethodCall.h" #include "XmlRpcMethodResponse.h" #include "common.h" /* self->unarchivedObjects does *not* store objects with xmlrpc base type! */ @interface XmlRpcDecoder(PrivateMethodes) - (id)_decodeObject:(XmlRpcValue *)_value; @end @interface NSData(NGExtensions) - (NSData *)dataByDecodingBase64; @end @implementation XmlRpcDecoder - (BOOL)profileXmlRpcCoding { return [[NSUserDefaults standardUserDefaults] boolForKey:@"ProfileXmlRpcCoding"]; } #if HAVE_NSXMLPARSER && 0 #warning using NSXMLParser! static BOOL useNSXMLParser = YES; #else static BOOL useNSXMLParser = NO; #endif static id saxHandler = nil; static id xmlRpcParser = nil; static Class ArrayClass = Nil; static Class DictionaryClass = Nil; static Class DataClass = Nil; static Class NumberClass = Nil; static Class StringClass = Nil; static Class DateClass = Nil; static BOOL doDebug = NO; + (void)initialize { if (ArrayClass == Nil) ArrayClass = [NSArray class]; if (DictionaryClass == Nil) DictionaryClass = [NSDictionary class]; if (DataClass == Nil) DataClass = [NSData class]; if (NumberClass == Nil) NumberClass = [NSNumber class]; if (StringClass == Nil) StringClass = [NSString class]; if (DateClass == Nil) DateClass = [NSCalendarDate class]; } - (id)init { if ((self = [super init])) { self->unarchivedObjects = [[NSMutableArray alloc] init]; self->awakeObjects = [[NSMutableSet alloc] init]; self->valueStack = [[NSMutableArray alloc] initWithCapacity:4]; self->timeZone = [[NSTimeZone timeZoneWithAbbreviation:@"GMT"] retain]; } return self; } - (NSStringEncoding)encodingForXMLEncodingString:(NSString *)_enc { _enc = [_enc lowercaseString]; if ([_enc isEqualToString:@"utf-8"]) return NSUTF8StringEncoding; else if ([_enc isEqualToString:@"iso-8859-1"]) return NSISOLatin1StringEncoding; #if !(NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) else if ([_enc isEqualToString:@"iso-8859-9"]) return NSISOLatin9StringEncoding; #endif else if ([_enc isEqualToString:@"ascii"]) return NSASCIIStringEncoding; else NSLog(@"%s: UNKNOWN XML ENCODING '%@'", __PRETTY_FUNCTION__, _enc); return 0; } - (id)initForReadingWithString:(NSString *)_string { if ((self = [self init])) { NSRange r; r = [_string rangeOfString:@"?>"]; if ([_string hasPrefix:@" 0) { NSStringEncoding enc; if ((enc = [self encodingForXMLEncodingString:xmlDecl]) != 0) { self->data = [[_string dataUsingEncoding:enc] retain]; if (self->data == nil) { NSLog(@"WARNING(%s): couldn't get data for string '%@', " @"encoding %i !", __PRETTY_FUNCTION__, _string, enc); [self release]; return nil; } } } } if (self->data == nil) self->data = [[_string dataUsingEncoding:NSUTF8StringEncoding] retain]; } return self; } - (id)initForReadingWithData:(NSData *)_data { if ((self = [self init])) { self->data = [_data retain]; } return self; } - (void)dealloc { [self->data release]; [self->valueStack release]; [self->unarchivedObjects release]; [self->awakeObjects release]; [self->timeZone release]; [super dealloc]; } /* accessors */ - (void)setDefaultTimeZone:(NSTimeZone *)_timeZone { [self->timeZone autorelease]; self->timeZone = [_timeZone retain]; } - (NSTimeZone *)defaultTimeZone { return self->timeZone; } /* *** */ - (void)_ensureSaxAndParser { if (saxHandler == nil) { if ((saxHandler = [[XmlRpcSaxHandler alloc] init]) == nil) { NSLog(@"%s: did not find sax handler ...", __PRETTY_FUNCTION__); return; } } if (useNSXMLParser) return; if (xmlRpcParser != nil) return; xmlRpcParser = [[SaxXMLReaderFactory standardXMLReaderFactory] createXMLReaderForMimeType:@"text/xml"]; if (xmlRpcParser == nil) { NSLog(@"%s: did not find an XML parser ...", __PRETTY_FUNCTION__); return; } [xmlRpcParser setContentHandler:saxHandler]; [xmlRpcParser setDTDHandler:saxHandler]; [xmlRpcParser setErrorHandler:saxHandler]; [xmlRpcParser retain]; } - (id)_decodeValueOfClass:(Class)_class { id obj; obj = [[self->valueStack lastObject] value]; if ([obj isKindOfClass:_class]) return obj; NSLog(@"WARNING(%s): obj (%@) is not of proper class ('%@'<-->'%@'", __PRETTY_FUNCTION__, obj, NSStringFromClass(_class), NSStringFromClass([obj class]), nil); return nil; } - (XmlRpcMethodCall *)_decodeMethodCall:(XmlRpcMethodCall *)_baseCall { NSArray *params; NSEnumerator *paramEnum; XmlRpcMethodCall *result = nil; XmlRpcValue *param = nil; NSMutableArray *decParams = nil; // decoded parameters params = [_baseCall parameters]; // XmlRpcValues!! decParams = [[NSMutableArray alloc] initWithCapacity:[params count]]; paramEnum = [params objectEnumerator]; while ((param = [paramEnum nextObject])) { id obj; if ((obj = [self _decodeObject:param]) != nil) [decParams addObject:obj]; } result = [[XmlRpcMethodCall alloc] initWithMethodName:[_baseCall methodName] parameters:decParams]; [decParams release]; return [result autorelease]; } - (XmlRpcMethodResponse *)_decodeMethodResponse:(XmlRpcMethodResponse *)_resp { static Class ExceptionClass = Nil; XmlRpcMethodResponse *response = nil; id result = [_resp result]; if (ExceptionClass == Nil) ExceptionClass = [NSException class]; if (![result isKindOfClass:ExceptionClass]) { result = [self _decodeObject:result]; // => XmlRpcValue } response = [[XmlRpcMethodResponse alloc] initWithResult:result]; return [response autorelease]; } - (NSStringEncoding)logEncoding { return NSUTF8StringEncoding; } - (id)decodeRootObject { id tmp = nil; if (doDebug) { NSLog(@"%s: begin (data: %i bytes, nesting: %i)", __PRETTY_FUNCTION__, [self->data length], self->nesting); } if ([self->data length] == 0) return nil; self->nesting++; [self _ensureSaxAndParser]; NSAssert(saxHandler, @"missing sax handler ..."); NSAssert(xmlRpcParser || useNSXMLParser, @"missing parser handler ..."); [saxHandler reset]; if (doDebug) NSLog(@"%s: SAX handler: %@", __PRETTY_FUNCTION__, saxHandler); #if HAVE_NSXMLPARSER if (useNSXMLParser) { NSXMLParser *parser; parser = [[NSXMLParser alloc] initWithData:self->data]; if (doDebug) NSLog(@"%s: using NSXMLParser: %@", __PRETTY_FUNCTION__, parser); [parser setDelegate:saxHandler]; [parser setShouldProcessNamespaces:NO]; [parser setShouldReportNamespacePrefixes:NO]; [parser setShouldResolveExternalEntities:NO]; [parser parse]; [parser release]; } else #endif [xmlRpcParser parseFromSource:self->data systemId:@""]; if ((tmp = [saxHandler methodCall]) != nil) { tmp = [self _decodeMethodCall:tmp]; } else if ((tmp = [saxHandler methodResponse]) != nil) { tmp = [self _decodeMethodResponse:tmp]; } else if (tmp == nil) { NSString *s; s = [[NSString alloc] initWithData:self->data encoding:[self logEncoding]]; NSLog(@"%s: couldn't parse source\n" @" parser: %@\n" @" handler: %@\n" @" data: %@" @" string: '%@'", __PRETTY_FUNCTION__, xmlRpcParser, saxHandler, self->data, s); [s release]; } else { NSLog(@"%s: neither call nor response (handler=%@): %@ ..", __PRETTY_FUNCTION__, saxHandler, tmp); } self->nesting--; // [saxHandler reset]; // hh asks: why is that commented out? if (doDebug) NSLog(@"%s: end: %@", __PRETTY_FUNCTION__, tmp); return tmp; } - (XmlRpcMethodCall *)decodeMethodCall { XmlRpcMethodCall *result; NSTimeInterval st = 0.0; if ([self profileXmlRpcCoding]) st = [[NSDate date] timeIntervalSince1970]; result = [self decodeRootObject]; if ([self profileXmlRpcCoding]) { NSTimeInterval diff; diff = [[NSDate date] timeIntervalSince1970] - st; printf("+++ decodeMethodCall: %0.5fs\n", diff); } return ([result isKindOfClass:[XmlRpcMethodCall class]]) ? result : (XmlRpcMethodCall *)nil; } - (XmlRpcMethodResponse *)decodeMethodResponse { XmlRpcMethodResponse *result; NSTimeInterval st = 0.0; if ([self profileXmlRpcCoding]) st = [[NSDate date] timeIntervalSince1970]; result = [self decodeRootObject]; if ([self profileXmlRpcCoding]) { NSTimeInterval diff; diff = [[NSDate date] timeIntervalSince1970] - st; printf("+++ decodeMethodResponse: %0.5fs\n", diff); } return [result isKindOfClass:[XmlRpcMethodResponse class]] ? result : (XmlRpcMethodResponse *)nil; } - (id)_decodeObject:(XmlRpcValue *)_value { id result; if (_value == nil) return nil; [self->valueStack addObject:_value]; result = [self decodeObject]; [self->valueStack removeLastObject]; return result; } - (id)decodeObject { Class objClass = Nil; id object = nil; self->nesting++; if ((self->nesting == 1) && ([self->valueStack count] == 0)) { object = [self decodeRootObject]; } else { NSString *className; id value = [self->valueStack lastObject]; className = [value className]; if ([className length] == 0) { object = [value value]; } else if ((objClass = NSClassFromString(className)) != Nil) { object = [objClass decodeObjectWithXmlRpcCoder:self]; } else { NSLog(@"%s: class (%@) specified by value (%@) wasn't found.", __PRETTY_FUNCTION__, className, value); object = [value value]; } if (object) [self->unarchivedObjects addObject:object]; } self->nesting--; return object; } - (NSDictionary *)decodeStruct { NSDictionary *dict; id result = nil; NSMutableDictionary *tmp; NSEnumerator *keyEnum; NSString *key; dict = (NSDictionary *)[[self->valueStack lastObject] value]; if (!([dict respondsToSelector:@selector(keyEnumerator)] && [dict respondsToSelector:@selector(objectForKey:)])) return nil; keyEnum = [dict keyEnumerator]; tmp = [[NSMutableDictionary alloc] initWithCapacity:8]; while ((key = [keyEnum nextObject])) { XmlRpcValue *v; id obj; v = [dict objectForKey:key]; if ((obj = [self _decodeObject:v]) != nil) [tmp setObject:obj forKey:key]; } result = [NSDictionary dictionaryWithDictionary:tmp]; [tmp release]; return result; } - (NSArray *)decodeArray { NSArray *array; id result = nil; NSMutableArray *tmp = nil; NSEnumerator *valEnum; XmlRpcValue *val = nil; array = (NSArray *)[[self->valueStack lastObject] value]; if (![array respondsToSelector:@selector(objectEnumerator)]) return nil; valEnum = [array objectEnumerator]; tmp = [[NSMutableArray alloc] initWithCapacity:8]; while ((val = [valEnum nextObject])) { id obj; if ((obj = [self _decodeObject:val]) != nil) [tmp addObject:obj]; } result = [NSArray arrayWithArray:tmp]; [tmp release]; return result; } - (NSData *)decodeBase64 { #if 0 /* data is already decoded in the XmlRpcValue */ tmp = [tmp dataByDecodingBase64]; #endif return [self _decodeValueOfClass:DataClass]; } - (BOOL)decodeBoolean { return [[self _decodeValueOfClass:NumberClass] boolValue]; } - (int)decodeInt { return [[self _decodeValueOfClass:NumberClass] intValue]; } - (double)decodeDouble { return [[self _decodeValueOfClass:NumberClass] doubleValue]; } - (NSString *)decodeString { return [self _decodeValueOfClass:StringClass]; } - (NSCalendarDate *)decodeDateTime { NSCalendarDate *date; NSTimeZone *tz; int secFromGMT; if ((date = [self _decodeValueOfClass:DateClass]) == nil) return nil; if ((tz = [date timeZone])) return date; if (![date respondsToSelector:@selector(setTimeZone:)]) { /* a plain date ... */ NSLog(@"cannot set timezone on date: %@", date); return date; } /* apply timezone correction */ secFromGMT = [self->timeZone secondsFromGMT]; [date setTimeZone:self->timeZone]; date = [date dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:-secFromGMT]; return date; } - (XmlRpcValue *)beginDecodingKey:(NSString *)_key { XmlRpcValue *newValue; NSDictionary *obj; obj = (NSDictionary *)[[self->valueStack lastObject] value]; NSAssert(_key != nil, @"_key is not allowed to be nil"); if (![obj isKindOfClass:DictionaryClass]) { NSLog(@"WARNING(%s): obj (%@) is not kind of class NSDictionary !!", __PRETTY_FUNCTION__, obj, nil); return nil; } if ((newValue = [obj objectForKey:_key]) == nil) /* got no value for key ... */ return nil; [self->valueStack addObject:newValue]; return newValue; } - (void)finishedDecodingKey { [self->valueStack removeLastObject]; } - (id)_decodeValueForKey:(NSString *)_key selector:(SEL)_selector { XmlRpcValue *newValue; id result; if ((newValue = [self beginDecodingKey:_key]) == nil) return nil; result = [[self performSelector:_selector] retain]; [self finishedDecodingKey]; return [result autorelease]; } - (NSDictionary *)decodeStructForKey:(NSString *)_key { return [self _decodeValueForKey:_key selector:@selector(decodeStruct)]; } - (NSArray *)decodeArrayForKey:(NSString *)_key { return [self _decodeValueForKey:_key selector:@selector(decodeArray)]; } - (NSData *)decodeBase64ForKey:(NSString *)_key { return [self _decodeValueForKey:_key selector:@selector(decodeBase64)]; } - (BOOL)decodeBooleanForKey:(NSString *)_key { XmlRpcValue *newValue; BOOL result; if ((newValue = [self beginDecodingKey:_key]) == nil) /* any useful alternatives ? */ return NO; result = [self decodeBoolean]; [self finishedDecodingKey]; return result; } - (int)decodeIntForKey:(NSString *)_key { XmlRpcValue *newValue; int result; if ((newValue = [self beginDecodingKey:_key]) == nil) /* any useful alternatives ? */ return NSNotFound; result = [self decodeInt]; [self finishedDecodingKey]; return result; } - (double)decodeDoubleForKey:(NSString *)_key { XmlRpcValue *newValue; double result; if ((newValue = [self beginDecodingKey:_key]) == nil) /* any useful alternatives ? */ return 0.0; result = [self decodeDouble]; [self finishedDecodingKey]; return result; } - (NSString *)decodeStringForKey:(NSString *)_key { return [self _decodeValueForKey:_key selector:@selector(decodeString)]; } - (NSCalendarDate *)decodeDateTimeForKey:(NSString *)_key { return [self _decodeValueForKey:_key selector:@selector(decodeDateTime)]; } - (id)decodeObjectForKey:(NSString *)_key { return [self _decodeValueForKey:_key selector:@selector(decodeObject)]; } /* operations */ - (void)ensureObjectAwake:(id)_object { if ([self->awakeObjects containsObject:_object]) return; if ([_object respondsToSelector:@selector(awakeFromXmlRpcDecoder:)]) [_object awakeFromXmlRpcDecoder:self]; [self->awakeObjects addObject:_object]; } - (void)awakeObjects { NSEnumerator *e; id obj; e = [self->unarchivedObjects objectEnumerator]; while ((obj = [e nextObject])) [self ensureObjectAwake:obj]; } - (void)finishInitializationOfObjects { NSEnumerator *e; id obj; e = [self->unarchivedObjects objectEnumerator]; while ((obj = [e nextObject])) { if ([obj respondsToSelector: @selector(finishInitializationWithXmlRpcDecoder:)]) [obj finishInitializationWithXmlRpcDecoder:self]; } } @end /* XmlRpcDecoder */