]> err.no Git - sope/blob - sope-xml/XmlRpc/XmlRpcDecoder.m
fixed copyrights for 2005
[sope] / sope-xml / XmlRpc / XmlRpcDecoder.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "XmlRpcCoder.h"
23 #include "XmlRpcValue.h"
24 #include "XmlRpcSaxHandler.h"
25 #include <SaxObjC/SaxXMLReaderFactory.h>
26 #include "XmlRpcMethodCall.h"
27 #include "XmlRpcMethodResponse.h"
28 #include "common.h"
29
30 /* self->unarchivedObjects does *not* store objects with xmlrpc base type! */
31
32 @interface XmlRpcDecoder(PrivateMethodes)
33 - (id)_decodeObject:(XmlRpcValue *)_value;
34 @end
35
36 @interface NSData(NGExtensions)
37 - (NSData *)dataByDecodingBase64;
38 @end
39
40 @implementation XmlRpcDecoder
41
42 - (BOOL)profileXmlRpcCoding {
43   return [[NSUserDefaults standardUserDefaults]
44                           boolForKey:@"ProfileXmlRpcCoding"];
45 }
46
47 #if HAVE_NSXMLPARSER && 0
48 #warning using NSXMLParser!
49 static BOOL useNSXMLParser = YES;
50 #else
51 static BOOL useNSXMLParser = NO;
52 #endif
53 static id saxHandler   = nil;
54 static id xmlRpcParser = nil;
55
56 static Class ArrayClass      = Nil;
57 static Class DictionaryClass = Nil;
58 static Class DataClass       = Nil;
59 static Class NumberClass     = Nil;
60 static Class StringClass     = Nil;
61 static Class DateClass       = Nil;
62 static BOOL  doDebug         = NO;
63
64 + (void)initialize {
65   if (ArrayClass      == Nil) ArrayClass      = [NSArray        class];
66   if (DictionaryClass == Nil) DictionaryClass = [NSDictionary   class];
67   if (DataClass       == Nil) DataClass       = [NSData         class];
68   if (NumberClass     == Nil) NumberClass     = [NSNumber       class];
69   if (StringClass     == Nil) StringClass     = [NSString       class];
70   if (DateClass       == Nil) DateClass       = [NSCalendarDate class];
71 }
72
73 - (id)init {
74   if ((self = [super init])) {
75     self->unarchivedObjects = [[NSMutableArray alloc] init];
76     self->awakeObjects      = [[NSMutableSet alloc] init];
77     self->valueStack        = [[NSMutableArray alloc] initWithCapacity:4];
78     self->timeZone = [[NSTimeZone timeZoneWithAbbreviation:@"GMT"] retain];
79   }
80   return self;
81 }
82
83 - (NSStringEncoding)encodingForXMLEncodingString:(NSString *)_enc {
84   _enc = [_enc lowercaseString];
85   if ([_enc isEqualToString:@"utf-8"])
86     return NSUTF8StringEncoding;
87   else if ([_enc isEqualToString:@"iso-8859-1"])
88     return NSISOLatin1StringEncoding;
89 #if !(NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY)
90   else if ([_enc isEqualToString:@"iso-8859-9"])
91     return NSISOLatin9StringEncoding;
92 #endif
93   else if ([_enc isEqualToString:@"ascii"])
94     return NSASCIIStringEncoding;
95   else
96     NSLog(@"%s: UNKNOWN XML ENCODING '%@'", __PRETTY_FUNCTION__, _enc);
97   return 0;
98 }
99
100 - (id)initForReadingWithString:(NSString *)_string {
101   if ((self = [self init])) {
102     NSRange r;
103     
104     r = [_string rangeOfString:@"?>"];
105     if ([_string hasPrefix:@"<?xml "] && r.length != 0) {
106       NSString *xmlDecl;
107       
108       xmlDecl = [_string substringToIndex:r.location];
109
110       r = [xmlDecl rangeOfString:@"encoding='"];
111       if (r.length != 0) {
112         xmlDecl = [_string substringFromIndex:(r.location + 10)];
113         r = [xmlDecl rangeOfString:@"'"];
114         xmlDecl = r.length == 0
115           ? nil
116           : [xmlDecl substringToIndex:r.location];
117       }
118       else {
119         r = [xmlDecl rangeOfString:@"encoding=\""];
120         if (r.length != 0) {
121           xmlDecl = [_string substringFromIndex:(r.location + 10)];
122           r = [xmlDecl rangeOfString:@"\""];
123           xmlDecl = r.length == 0
124             ? nil
125             : [xmlDecl substringToIndex:r.location];
126         }
127         else
128           xmlDecl = nil;
129       }
130       
131       if ([xmlDecl length] > 0) {
132         NSStringEncoding enc;
133         
134         if ((enc = [self encodingForXMLEncodingString:xmlDecl]) != 0) {
135           self->data = [[_string dataUsingEncoding:enc] retain];
136           if (self->data == nil) {
137             NSLog(@"WARNING(%s): couldn't get data for string '%@', "
138                   @"encoding %i !", __PRETTY_FUNCTION__, _string, enc);
139             [self release];
140             return nil;
141           }
142         }
143       }
144     }
145     
146     if (self->data == nil)
147       self->data = [[_string dataUsingEncoding:NSUTF8StringEncoding] retain];
148   }
149   return self;
150 }
151 - (id)initForReadingWithData:(NSData *)_data {
152   if ((self = [self init])) {
153     self->data = [_data retain];
154   }
155   return self;
156 }
157
158 - (void)dealloc {
159   [self->data         release];
160   [self->valueStack   release];
161   [self->unarchivedObjects release];
162   [self->awakeObjects release];
163   
164   [self->timeZone release];
165   [super dealloc];
166 }
167
168 /* accessors */
169
170 - (void)setDefaultTimeZone:(NSTimeZone *)_timeZone {
171   [self->timeZone autorelease];
172   self->timeZone = [_timeZone retain];
173 }
174
175 - (NSTimeZone *)defaultTimeZone {
176   return self->timeZone;
177 }
178
179 /* *** */
180
181 - (void)_ensureSaxAndParser {
182   if (saxHandler == nil) {
183     if ((saxHandler = [[XmlRpcSaxHandler alloc] init]) == nil) {
184       NSLog(@"%s: did not find sax handler ...", __PRETTY_FUNCTION__);
185       return;
186     }
187   }
188
189   if (useNSXMLParser)      return;
190   if (xmlRpcParser != nil) return;
191   
192   xmlRpcParser =
193     [[SaxXMLReaderFactory standardXMLReaderFactory] 
194                           createXMLReaderForMimeType:@"text/xml"];
195   if (xmlRpcParser == nil) {
196     NSLog(@"%s: did not find an XML parser ...", __PRETTY_FUNCTION__);
197     return;
198   }
199
200   [xmlRpcParser setContentHandler:saxHandler];
201   [xmlRpcParser setDTDHandler:saxHandler];
202   [xmlRpcParser setErrorHandler:saxHandler];
203   [xmlRpcParser retain];
204 }
205
206 - (id)_decodeValueOfClass:(Class)_class {
207   id obj;
208   
209   obj = [[self->valueStack lastObject] value];
210
211   if ([obj isKindOfClass:_class])
212     return obj;
213   
214   NSLog(@"WARNING(%s): obj (%@) is not of proper class ('%@'<-->'%@'",
215         __PRETTY_FUNCTION__,
216         obj,
217         NSStringFromClass(_class),
218         NSStringFromClass([obj class]),
219         nil);
220   return nil;
221 }
222
223 - (XmlRpcMethodCall *)_decodeMethodCall:(XmlRpcMethodCall *)_baseCall {
224   NSArray          *params;
225   NSEnumerator     *paramEnum;
226   XmlRpcMethodCall *result    = nil;
227   XmlRpcValue    *param       = nil;
228   NSMutableArray *decParams   = nil;  // decoded parameters
229
230   params    = [_baseCall parameters]; // XmlRpcValues!!
231   decParams = [[NSMutableArray alloc] initWithCapacity:[params count]];
232   
233   paramEnum = [params objectEnumerator];
234   while ((param = [paramEnum nextObject])) {
235     id obj;
236
237     if ((obj = [self _decodeObject:param]) != nil)
238       [decParams addObject:obj];
239   }
240
241   result = [[XmlRpcMethodCall alloc] initWithMethodName:[_baseCall methodName]
242                                      parameters:decParams];
243
244   [decParams release];
245
246   return [result autorelease];
247 }
248
249 - (XmlRpcMethodResponse *)_decodeMethodResponse:(XmlRpcMethodResponse *)_resp {
250   static Class ExceptionClass = Nil;
251   XmlRpcMethodResponse *response = nil;
252   id                   result    = [_resp result];
253   
254   if (ExceptionClass == Nil)
255     ExceptionClass = [NSException class];
256
257   if (![result isKindOfClass:ExceptionClass]) {
258
259     result = [self _decodeObject:result]; // => XmlRpcValue
260   }
261   response = [[XmlRpcMethodResponse alloc] initWithResult:result];
262   
263   return [response autorelease];
264 }
265
266 - (NSStringEncoding)logEncoding {
267   return NSUTF8StringEncoding;
268 }
269 - (id)decodeRootObject {
270   id tmp = nil;
271   
272   if (doDebug) {
273     NSLog(@"%s: begin (data: %i bytes, nesting: %i)", __PRETTY_FUNCTION__, 
274           [self->data length], self->nesting);
275   }
276   if ([self->data length] == 0) return nil;
277   
278   self->nesting++;
279   
280   [self _ensureSaxAndParser];
281   NSAssert(saxHandler,   @"missing sax handler ...");
282   NSAssert(xmlRpcParser || useNSXMLParser, @"missing parser handler ...");
283   
284   [saxHandler reset];
285   if (doDebug) NSLog(@"%s:  SAX handler: %@", __PRETTY_FUNCTION__, saxHandler);
286 #if HAVE_NSXMLPARSER
287   if (useNSXMLParser) {
288     NSXMLParser *parser;
289     
290     parser = [[NSXMLParser alloc] initWithData:self->data];
291     if (doDebug) 
292       NSLog(@"%s:  using NSXMLParser: %@", __PRETTY_FUNCTION__, parser);
293     [parser setDelegate:saxHandler];
294     [parser setShouldProcessNamespaces:NO];
295     [parser setShouldReportNamespacePrefixes:NO];
296     [parser setShouldResolveExternalEntities:NO];
297     [parser parse];
298     [parser release];
299   }
300   else
301 #endif
302     [xmlRpcParser parseFromSource:self->data systemId:@"<xmlrpc-data>"];
303   
304   if ((tmp = [saxHandler methodCall]) != nil) {
305     tmp = [self _decodeMethodCall:tmp];
306   }
307   else if ((tmp = [saxHandler methodResponse]) != nil) {
308     tmp = [self _decodeMethodResponse:tmp];
309   }
310   else if (tmp == nil) {
311     NSString *s;
312     
313     s = [[NSString alloc] initWithData:self->data encoding:[self logEncoding]];
314     NSLog(@"%s: couldn't parse source\n"
315           @"  parser:  %@\n"
316           @"  handler: %@\n"
317           @"  data:    %@"
318           @"  string:  '%@'",
319           __PRETTY_FUNCTION__, xmlRpcParser, saxHandler, self->data, s);
320     [s release];
321   }
322   else {
323     NSLog(@"%s: neither call nor response (handler=%@): %@ ..",
324           __PRETTY_FUNCTION__,
325           saxHandler, tmp);
326   }
327   
328   self->nesting--;
329
330   // [saxHandler reset]; // hh asks: why is that commented out?
331   if (doDebug) NSLog(@"%s: end: %@", __PRETTY_FUNCTION__, tmp);
332   return tmp;  
333 }
334
335 - (XmlRpcMethodCall *)decodeMethodCall {
336   XmlRpcMethodCall *result;
337   NSTimeInterval st     = 0.0;
338     
339   if ([self profileXmlRpcCoding])
340     st = [[NSDate date] timeIntervalSince1970];  
341
342   result = [self decodeRootObject];
343
344   if ([self profileXmlRpcCoding]) {
345     NSTimeInterval diff;
346     diff = [[NSDate date] timeIntervalSince1970] - st;
347     
348     printf("+++     decodeMethodCall: %0.5fs\n", diff);
349   }
350   
351   return ([result isKindOfClass:[XmlRpcMethodCall class]])
352     ? result
353     : nil;
354 }
355
356 - (XmlRpcMethodResponse *)decodeMethodResponse {
357   XmlRpcMethodResponse *result;
358   NSTimeInterval st    = 0.0;
359
360   if ([self profileXmlRpcCoding])
361     st = [[NSDate date] timeIntervalSince1970];
362
363   result = [self decodeRootObject];
364
365   if ([self profileXmlRpcCoding]) {
366     NSTimeInterval diff;
367     diff = [[NSDate date] timeIntervalSince1970] - st;
368     
369     printf("+++ decodeMethodResponse: %0.5fs\n", diff);
370   }
371   
372   return [result isKindOfClass:[XmlRpcMethodResponse class]]
373     ? result
374     : nil;
375 }
376
377 - (id)_decodeObject:(XmlRpcValue *)_value {
378   id result;
379   
380   if (_value == nil) return nil;
381   
382   [self->valueStack addObject:_value];
383   result = [self decodeObject];
384   [self->valueStack removeLastObject];
385   return result;
386 }
387
388 - (id)decodeObject {
389   Class objClass = Nil;
390   id    object   = nil;
391   
392   self->nesting++;
393
394   if ((self->nesting == 1) && ([self->valueStack count] == 0)) {
395     object = [self decodeRootObject];
396   }
397   else {
398     NSString *className;
399     id value = [self->valueStack lastObject];
400     
401     className = [value className];
402
403     if ([className length] == 0) {
404       object = [value value];
405     }
406     else if ((objClass = NSClassFromString(className)) != Nil) {
407       object = [objClass decodeObjectWithXmlRpcCoder:self];
408     }
409     else {
410       NSLog(@"%s: class (%@) specified by value (%@) wasn't found.",
411             __PRETTY_FUNCTION__, className, value);
412       object = [value value];
413     }
414
415     if (object) [self->unarchivedObjects addObject:object];
416   }
417  
418   self->nesting--;
419   
420   return object;
421 }
422
423 - (NSDictionary *)decodeStruct {
424   NSDictionary *dict;
425   id           result = nil;
426   NSMutableDictionary *tmp;
427   NSEnumerator        *keyEnum;
428   NSString            *key;
429   
430   dict  = (NSDictionary *)[[self->valueStack lastObject] value];
431   
432   if (!([dict respondsToSelector:@selector(keyEnumerator)] &&
433         [dict respondsToSelector:@selector(objectForKey:)]))
434     return nil;
435   
436   keyEnum  = [dict keyEnumerator];
437   tmp = [[NSMutableDictionary alloc] initWithCapacity:8];
438     
439   while ((key = [keyEnum nextObject])) {
440     XmlRpcValue *v;
441     id          obj;
442
443     v = [dict objectForKey:key];
444     if ((obj = [self _decodeObject:v]) != nil)
445         [tmp setObject:obj forKey:key];
446   }
447   result = [NSDictionary dictionaryWithDictionary:tmp];
448   [tmp release];
449   return result;
450 }
451
452 - (NSArray *)decodeArray {
453   NSArray        *array;
454   id             result = nil;
455   NSMutableArray *tmp      = nil;
456   NSEnumerator   *valEnum;
457   XmlRpcValue    *val      = nil;
458   
459   array = (NSArray *)[[self->valueStack lastObject] value];
460   if (![array respondsToSelector:@selector(objectEnumerator)])
461     return nil;
462
463   valEnum = [array objectEnumerator];
464   tmp = [[NSMutableArray alloc] initWithCapacity:8];
465     
466   while ((val = [valEnum nextObject])) {
467     id obj;
468
469     if ((obj = [self _decodeObject:val]) != nil)
470         [tmp addObject:obj];
471   }
472   result = [NSArray arrayWithArray:tmp];
473   [tmp release];
474   return result;
475 }
476
477 - (NSData *)decodeBase64 {
478   return [[self _decodeValueOfClass:DataClass]
479                 dataByDecodingBase64];
480 }
481
482 - (BOOL)decodeBoolean {
483   return [[self _decodeValueOfClass:NumberClass] boolValue];
484 }
485
486 - (int)decodeInt {
487   return [[self _decodeValueOfClass:NumberClass] intValue];
488 }
489
490 - (double)decodeDouble {
491   return [[self _decodeValueOfClass:NumberClass] doubleValue];
492 }
493
494 - (NSString *)decodeString {
495   return [self _decodeValueOfClass:StringClass];
496 }
497
498 - (NSCalendarDate *)decodeDateTime {
499   NSCalendarDate *date;
500   NSTimeZone     *tz;
501   int            secFromGMT;
502   
503   if ((date = [self _decodeValueOfClass:DateClass]) == nil)
504     return nil;
505   
506   if ((tz = [date timeZone]))
507     return date;
508   
509   if (![date respondsToSelector:@selector(setTimeZone:)]) {
510     /* a plain date ... */
511     NSLog(@"cannot set timezone on date: %@", date);
512     return date;
513   }
514   
515   /* apply timezone correction */
516   secFromGMT = [self->timeZone secondsFromGMT];
517   [date setTimeZone:self->timeZone];
518   date = [date dateByAddingYears:0 months:0 days:0
519                hours:0 minutes:0 seconds:-secFromGMT];
520   return date;
521 }
522
523 - (XmlRpcValue *)beginDecodingKey:(NSString *)_key {
524   XmlRpcValue  *newValue;
525   NSDictionary *obj;
526   
527   obj = (NSDictionary *)[[self->valueStack lastObject] value];
528   NSAssert(_key != nil, @"_key is not allowed to be nil");
529   
530   if (![obj isKindOfClass:DictionaryClass]) {
531     NSLog(@"WARNING(%s): obj (%@) is not kind of class NSDictionary !!",
532           __PRETTY_FUNCTION__, obj, nil);
533     return nil;
534   }
535   
536   if ((newValue = [obj objectForKey:_key]) == nil)
537     /* got no value for key ... */
538     return nil;
539   
540   [self->valueStack addObject:newValue];
541   return newValue;
542 }
543 - (void)finishedDecodingKey {
544   [self->valueStack removeLastObject];
545 }
546
547 - (id)_decodeValueForKey:(NSString *)_key selector:(SEL)_selector {
548   XmlRpcValue *newValue;
549   id result;
550   
551   if ((newValue = [self beginDecodingKey:_key]) == nil)
552     return nil;
553   
554   result = [[self performSelector:_selector] retain];
555   [self finishedDecodingKey];
556   return [result autorelease];
557 }
558
559 - (NSDictionary *)decodeStructForKey:(NSString *)_key {
560   return [self _decodeValueForKey:_key selector:@selector(decodeStruct)];
561 }
562
563 - (NSArray *)decodeArrayForKey:(NSString *)_key {
564   return [self _decodeValueForKey:_key selector:@selector(decodeArray)];
565 }
566
567 - (NSData *)decodeBase64ForKey:(NSString *)_key {
568   return [self _decodeValueForKey:_key selector:@selector(decodeBase64)];
569 }
570
571 - (BOOL)decodeBooleanForKey:(NSString *)_key {
572   XmlRpcValue *newValue;
573   BOOL result;
574   
575   if ((newValue = [self beginDecodingKey:_key]) == nil)
576     /* any useful alternatives ? */
577     return NO;
578   
579   result = [self decodeBoolean];
580   [self finishedDecodingKey];
581   return result;
582 }
583
584 - (int)decodeIntForKey:(NSString *)_key {
585   XmlRpcValue *newValue;
586   int result;
587   
588   if ((newValue = [self beginDecodingKey:_key]) == nil)
589     /* any useful alternatives ? */
590     return NSNotFound;
591   
592   result = [self decodeInt];
593   [self finishedDecodingKey];
594   return result;
595 }
596
597 - (double)decodeDoubleForKey:(NSString *)_key {
598   XmlRpcValue *newValue;
599   double result;
600   
601   if ((newValue = [self beginDecodingKey:_key]) == nil)
602     /* any useful alternatives ? */
603     return 0.0;
604   
605   result = [self decodeDouble];
606   [self finishedDecodingKey];
607   return result;
608 }
609
610 - (NSString *)decodeStringForKey:(NSString *)_key {
611   return [self _decodeValueForKey:_key selector:@selector(decodeString)];
612 }
613
614 - (NSCalendarDate *)decodeDateTimeForKey:(NSString *)_key {
615   return [self _decodeValueForKey:_key selector:@selector(decodeDateTime)];
616 }
617
618 - (id)decodeObjectForKey:(NSString *)_key {
619  return [self _decodeValueForKey:_key selector:@selector(decodeObject)];
620 }
621
622 /* operations */
623
624 - (void)ensureObjectAwake:(id)_object {
625   if ([self->awakeObjects containsObject:_object])
626     return;
627   
628   if ([_object respondsToSelector:@selector(awakeFromXmlRpcDecoder:)])
629     [_object awakeFromXmlRpcDecoder:self];
630   [self->awakeObjects addObject:_object];
631 }
632 - (void)awakeObjects {
633   NSEnumerator *e;
634   id obj;
635
636   e = [self->unarchivedObjects objectEnumerator];
637   while ((obj = [e nextObject]))
638     [self ensureObjectAwake:obj];
639 }
640
641 - (void)finishInitializationOfObjects {
642   NSEnumerator *e;
643   id obj;
644
645   e = [self->unarchivedObjects objectEnumerator];
646   while ((obj = [e nextObject])) {
647     if ([obj respondsToSelector:
648                @selector(finishInitializationWithXmlRpcDecoder:)])
649       [obj finishInitializationWithXmlRpcDecoder:self];
650   }
651 }
652
653 @end /* XmlRpcDecoder */