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