]> err.no Git - sope/blob - sope-xml/XmlRpc/XmlRpcSaxHandler.m
changed "Wrapper" buildstyle to produce universal (fat) binaries by default. Also...
[sope] / sope-xml / XmlRpc / XmlRpcSaxHandler.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 "XmlRpcSaxHandler.h"
23 #include "XmlRpcMethodCall.h"
24 #include "XmlRpcMethodResponse.h"
25 #include "NSObject+XmlRpc.h"
26 #include "XmlRpcValue.h"
27 #include "common.h"
28
29 @implementation XmlRpcSaxHandler
30 /*"
31   The SAX handler used to decode XML-RPC responses and requests. If the
32   parsing finishes successfully, either -methodCall or -methodResponse will
33   return an properly initialized object representing the XML-RPC response.
34   
35   The SAX handler is used by the XmlRpcDecoder class internally, in most
36   cases you shouldn't need to access it directly.
37 "*/
38
39 static Class ArrayClass      = Nil;
40 static Class DictionaryClass = Nil;
41 static BOOL  doDebug         = NO;
42
43 + (void)initialize {
44   if (ArrayClass      == Nil) ArrayClass      = [NSMutableArray      class];
45   if (DictionaryClass == Nil) DictionaryClass = [NSMutableDictionary class];
46 }
47
48 - (void)reset {
49   if (doDebug) NSLog(@"%s: begin ...", __PRETTY_FUNCTION__);
50   [self->response   release]; self->response   = nil;
51   [self->call       release]; self->call       = nil;
52   [self->methodName release]; self->methodName = nil;
53   [self->params     release]; self->params     = nil;
54   
55   // for recursive structures (struct, array)
56   [self->valueStack removeAllObjects];
57
58   [self->className release]; self->className = nil;
59   
60   [self->memberNameStack  removeAllObjects];
61   [self->memberValueStack removeAllObjects];
62   [self->timeZone release]; self->timeZone = nil;
63   [self->dateTime release]; self->dateTime = nil;
64   
65   [self->characters setString:@""];
66   
67   self->valueNestingLevel = 0;
68   self->nextCharactersProcessor = NULL;
69   self->invalidCall = NO;
70   [self->tagStack removeAllObjects];
71 }
72 - (void)dealloc {
73   [self reset];
74   
75   [self->characters release];
76   [self->tagStack   release];
77   [self->valueStack release];
78   
79   [self->memberNameStack  release];
80   [self->memberValueStack release];
81   
82   [super dealloc];
83 }
84
85 /* accessors */
86
87 - (XmlRpcMethodCall *)methodCall {
88   return self->call;
89 }
90
91 - (XmlRpcMethodResponse *)methodResponse {
92   return self->response;
93 }
94
95 - (id)result {
96   return [self->params lastObject]; // => NSException || XmlRpcValue
97 }
98
99 /* *** */
100
101 - (void)_addValueToParas:(id)_value {
102   id topValue = [[self->valueStack lastObject] value];
103
104   if ([topValue isKindOfClass:ArrayClass])
105     [topValue addObject:_value];
106   else if ([topValue isKindOfClass:DictionaryClass])
107     [self->memberValueStack addObject:_value];
108   else
109     [self->params addObject:_value];
110 }
111
112 /* document */
113
114 - (void)startDocument {
115   if (doDebug) NSLog(@"%s: begin ...", __PRETTY_FUNCTION__);
116   [self reset];
117   
118   if (self->tagStack == nil)
119     self->tagStack = [[NSMutableArray alloc] initWithCapacity:8];
120   if (self->valueStack == nil)
121     self->valueStack = [[NSMutableArray alloc] initWithCapacity:8];
122   if (self->characters == nil)
123     self->characters = [[NSMutableString alloc] initWithCapacity:128];
124   
125   if (doDebug) NSLog(@"%s: done ...", __PRETTY_FUNCTION__);
126 }
127 - (void)endDocument {
128   if (doDebug) NSLog(@"%s: begin ...", __PRETTY_FUNCTION__);
129   
130   if ([self->tagStack count] > 0) {
131     self->invalidCall = YES;
132     NSLog(@"Warning(%s): tagStack is not empty (%@)",
133           __PRETTY_FUNCTION__,
134           self->tagStack);
135   }
136
137   if (self->call != nil && self->response != nil) {
138     self->invalidCall = YES;
139     NSLog(@"Warning(%s): got methodCall *AND* methodResponse!!! (%@<->%@)",
140           __PRETTY_FUNCTION__,
141           self->call,
142           self->response);
143   }
144
145   if (self->invalidCall) {
146     if (doDebug) NSLog(@"%s:   marked as invalid call!", __PRETTY_FUNCTION__);
147     [self->call     release]; self->call     = nil;
148     [self->response release]; self->response = nil;
149   }
150   if (doDebug) NSLog(@"%s: done ...", __PRETTY_FUNCTION__);
151 }
152
153 /* elements */
154
155 - (void)start_name:(id<SaxAttributes>)_attrs {
156   self->nextCharactersProcessor = @selector(_name:length:);
157 }
158 - (void)end_name {
159   self->nextCharactersProcessor = NULL;
160 }
161 - (void)_name:(unichar *)_chars length:(int)_len {
162   NSString *name;
163   name = [NSString stringWithCharacters:_chars length:_len];
164   [self->memberNameStack addObject:name];
165 }
166
167 - (void)start_params:(id<SaxAttributes>)_attrs {
168   if (self->params) {
169     self->invalidCall = YES;
170     return;
171   }
172   self->params = [[NSMutableArray alloc] initWithCapacity:8];
173 }
174 - (void)end_params {
175   if (self->params == nil)
176     self->invalidCall =YES;
177 }
178
179 - (void)start_value:(id<SaxAttributes>)_attrs {
180   self->valueNestingLevel++;
181   self->nextCharactersProcessor = @selector(_baseValue:length:);
182 }
183 - (void)end_value {
184   self->valueNestingLevel--;
185 }
186
187 - (void)_dateValue:(unichar *)_chars length:(int)_len {
188   if (self->dateTime)
189     return;
190   
191   self->dateTime = [[NSObject objectWithXmlRpcType:@"dateTime.iso8601"
192                               characters:_chars length:_len]
193                               retain];
194 }
195
196 - (void)_baseValue:(unichar *)_chars length:(int)_len {
197   id value;
198
199   if (self->valueNestingLevel == 0) {
200     NSLog(@"%s: invalidCall......... self->valueNestingLevel = 0",
201           __PRETTY_FUNCTION__);
202     return;
203   }
204   
205   value = [NSObject objectWithXmlRpcType:[self->tagStack lastObject]
206                     characters:_chars length:_len];
207   
208   if (value == nil)
209     value = [NSNull null];
210
211   value = [[XmlRpcValue alloc] initWithValue:value className:self->className];
212     
213   if (self->params == nil) {
214     NSLog(@"%s: invalidCall......... self->params = nil",
215           __PRETTY_FUNCTION__);
216     return;
217   }
218   [self _addValueToParas:value];
219   
220   [value release];
221 }
222
223 - (void)start_i4:(id<SaxAttributes>)_attrs {
224   self->nextCharactersProcessor = @selector(_baseValue:length:);
225 }
226 - (void)start_int:(id<SaxAttributes>)_attrs {
227   self->nextCharactersProcessor = @selector(_baseValue:length:);
228 }
229 - (void)start_double:(id<SaxAttributes>)_attrs {
230   self->nextCharactersProcessor = @selector(_baseValue:length:);
231 }
232 - (void)start_base64:(id<SaxAttributes>)_attrs {
233   self->nextCharactersProcessor = @selector(_baseValue:length:);
234 }
235 - (void)start_boolean:(id<SaxAttributes>)_attrs {
236   self->nextCharactersProcessor = @selector(_baseValue:length:);
237 }
238 - (void)start_string:(id<SaxAttributes>)_attrs {
239   self->nextCharactersProcessor = @selector(_baseValue:length:);
240 }
241 - (void)start_dateTime:(id<SaxAttributes>)_attrs {
242   NSString *tz;
243   int      idx;
244   
245   [self->timeZone release]; self->timeZone = nil;
246   [self->dateTime release]; self->dateTime = nil;
247   
248   tz = ((idx = [_attrs indexOfRawName:@"timeZone"]) != NSNotFound)
249       ? [_attrs valueAtIndex:idx]
250       : nil;
251   
252   if (tz) {
253     self->timeZone = [[NSTimeZone timeZoneWithAbbreviation:tz] retain];
254   }
255   self->nextCharactersProcessor = @selector(_dateValue:length:);
256 }
257
258 - (void)end_dateTime {
259   if (self->dateTime) {
260     NSCalendarDate *date;
261     XmlRpcValue    *value;
262     int            secFromGMT;
263     
264     if ([self->dateTime respondsToSelector:@selector(setTimeZone:)]) {
265       secFromGMT = [self->timeZone secondsFromGMT];
266       [self->dateTime setTimeZone:self->timeZone];
267       date = [self->dateTime dateByAddingYears:0 months:0 days:0
268                              hours:0 minutes:0 seconds:-secFromGMT];
269     }
270     else {
271       NSLog(@"WARNING(%s): cannot set timezone on date: %@", 
272             __PRETTY_FUNCTION__, self->dateTime);
273       date = self->dateTime;
274     }
275     
276     value = [[XmlRpcValue alloc] initWithValue:date
277                                  className:@"NSCalendarDate"];
278     [value autorelease];
279     [self _addValueToParas:value];
280   }
281   
282   [self->timeZone release]; self->timeZone = nil;
283   [self->dateTime release]; self->dateTime = nil;
284   self->nextCharactersProcessor = NULL;
285 }
286
287
288 - (void)start_array:(id<SaxAttributes>)_attrs {
289   id value = [NSMutableArray arrayWithCapacity:8];
290
291   value = [[XmlRpcValue alloc] initWithValue:value className:self->className];
292   
293   [self _addValueToParas:value];
294   [self->valueStack addObject:value];
295   
296   self->nextCharactersProcessor = NULL;
297   [value release];
298 }
299
300 - (void)end_array {
301   if ([self->valueStack count] > 0)
302     [self->valueStack removeLastObject];
303   else {
304     NSLog(@"%s: valueStack should be empty: %@",
305           __PRETTY_FUNCTION__,
306           self->valueStack);
307   }
308 }
309
310 - (void)start_struct:(id<SaxAttributes>)_attrs {
311   id value = [NSMutableDictionary dictionaryWithCapacity:8];
312
313   value = [[XmlRpcValue alloc] initWithValue:value className:self->className];
314   
315   [self _addValueToParas:value];
316   [self->valueStack addObject:value];
317
318   self->nextCharactersProcessor = NULL;
319   [value release];
320 }
321
322 - (void)end_struct {
323   if ([self->valueStack count] > 0)
324     [self->valueStack removeLastObject];
325   else {
326     NSLog(@"%s: valueStack should be empty: %@",
327           __PRETTY_FUNCTION__,
328           self->valueStack);
329   }
330 }
331
332 - (void)start_member:(id<SaxAttributes>)_attrs {
333   if (![[[self->valueStack lastObject] value] isKindOfClass:DictionaryClass]) {
334     self->invalidCall = YES;
335   }
336   else {
337     if (self->memberNameStack == nil)
338       self->memberNameStack = [[NSMutableArray alloc] initWithCapacity:8];
339     if (self->memberValueStack == nil)
340       self->memberValueStack = [[NSMutableArray alloc] initWithCapacity:8];
341   }
342   self->nextCharactersProcessor = NULL;
343 }
344
345 - (void)end_member {
346   id tmp; // TODO: can't we type the var?
347
348   tmp = [[self->valueStack lastObject] value];
349
350   if ([self->memberNameStack count] != [self->memberValueStack count]) {
351     NSLog(@"Warning(%s): memberNameStack.count != memberValueStack.count"
352           @" (%@ <--> %@)",
353           __PRETTY_FUNCTION__,
354           self->memberNameStack,
355           self->memberValueStack,
356           nil);
357     [self->memberValueStack release]; self->memberValueStack = nil;
358     [self->memberNameStack  release]; self->memberNameStack  = nil;
359     self->invalidCall = YES;
360   }
361   else if ([self->memberNameStack count] == 0) {
362     NSLog(@"Warning(%s): memberNameStack and memberValueStack are empty!",
363           __PRETTY_FUNCTION__,
364           nil);
365     [self->memberValueStack release]; self->memberValueStack = nil;
366     [self->memberNameStack  release]; self->memberNameStack  = nil;
367     self->invalidCall = YES;
368   }
369   else if (![tmp isKindOfClass:DictionaryClass])
370     self->invalidCall = YES;
371   else {
372     [(NSMutableDictionary *)tmp
373                             setObject:[self->memberValueStack lastObject]
374                             forKey:[self->memberNameStack lastObject]];
375     
376     [self->memberNameStack  removeLastObject];
377     [self->memberValueStack removeLastObject];
378   }
379 }
380
381 - (void)start_methodCall:(id<SaxAttributes>)_attrs {
382   /* can't create call here, args unknown !!! */
383   if (self->call != nil) {
384     if (doDebug) 
385       NSLog(@"%s: method-call already setup!", __PRETTY_FUNCTION__);
386     self->invalidCall = YES;
387     return;
388   }
389   if (doDebug) NSLog(@"%s: ...", __PRETTY_FUNCTION__);
390 }
391 - (void)end_methodCall {
392   if (self->call != nil) {
393     if (doDebug) 
394       NSLog(@"%s: method-call already setup!", __PRETTY_FUNCTION__);
395     self->invalidCall = YES;
396     return;
397   }
398   
399   self->call = [[XmlRpcMethodCall alloc] initWithMethodName:self->methodName
400                                          parameters:self->params];
401   
402   /* reset args */
403   [self->methodName release]; self->methodName = nil;
404   [self->params     release]; self->params     = nil;
405 }
406
407 - (void)start_methodResponse:(id<SaxAttributes>)_attrs {
408   if (self->response != nil) {
409     if (doDebug) 
410       NSLog(@"%s: method-response already setup!", __PRETTY_FUNCTION__);
411     self->invalidCall = YES;
412     return;
413   }
414 }
415
416 - (void)end_methodResponse {
417   if (doDebug) NSLog(@"%s: begin ...", __PRETTY_FUNCTION__);
418   
419   if (self->response != nil) {
420     if (doDebug) 
421       NSLog(@"%s:   method-response already setup!", __PRETTY_FUNCTION__);
422     self->invalidCall = YES;
423     return;
424   }
425
426   if ([self->params count] > 1) {
427     if (doDebug) {
428       NSLog(@"%s:   has more than one params (%i)!", __PRETTY_FUNCTION__, 
429       [self->params count]);
430     }
431     self->invalidCall = YES;
432   }
433   
434   if (self->invalidCall) {
435     NSException *error;
436
437     if (doDebug)
438       NSLog(@"%s:   response marked as invalid!", __PRETTY_FUNCTION__);
439     
440     error = [NSException exceptionWithName:@"error while parsing response"
441                          reason:@"error while parsing response"
442                          userInfo:nil];
443     
444     [self->params release];
445     self->params = [[NSMutableArray arrayWithObject:error] retain];
446   }
447
448   self->response =
449     [[XmlRpcMethodResponse alloc] initWithResult:[self->params lastObject]];
450   if (doDebug)
451     NSLog(@"%s:   response: %@", __PRETTY_FUNCTION__, self->response);
452
453   /* reset args */
454   [self->params release]; self->params = nil;
455   if (doDebug) NSLog(@"%s: done.", __PRETTY_FUNCTION__);
456 }
457
458 - (void)start_methodName:(id<SaxAttributes>)_attrs {
459   if (self->call != nil) {
460     self->invalidCall = YES;
461     return;
462   }
463   self->nextCharactersProcessor = @selector(_methodName:length:);
464 }
465
466 - (void)end_methodName {
467   self->nextCharactersProcessor = NULL;
468 }
469 - (void)_methodName:(unichar *)_chars length:(int)_len {
470   [self->methodName release];
471   self->methodName = [[NSString alloc] initWithCharacters:_chars length:_len];
472 }
473
474 - (void)start_fault:(id<SaxAttributes>)_attrs {
475   if (self->params) {
476     self->invalidCall = YES;
477     return;
478   }
479   else
480     self->params = [[NSMutableArray alloc] initWithCapacity:2];
481   
482   self->nextCharactersProcessor = NULL;
483 }
484 - (void)end_fault {
485   if (self->params == nil)
486     self->invalidCall = YES;
487
488   self->nextCharactersProcessor = NULL;
489
490   /* fixup result class */
491   if ([self->params count] != 1) {
492     NSLog(@"XML-RPC: incorrect params count (should be 1 for faults) ?: %@",
493           self->params);
494   }
495   else {
496     XmlRpcValue *fault;
497     
498     fault = [self->params objectAtIndex:0];
499     if (![fault isException]) {
500       if ([fault isDictionary]) {
501         [fault setClassName:@"NSException"];
502       }
503       else {
504         NSException *e;
505         NSString *name;
506         NSString *reason;
507         
508         NSLog(@"XML-RPC: got incorrect fault object (class=%@) ?: %@",
509               [fault className], fault);
510         name = [NSString stringWithFormat:@"XmlRpcFault<%@>", 
511                            [fault valueForKey:@"faultCode"]];
512         if (name == nil) name = @"GenericXmlRpcFault";
513         reason = [fault valueForKey:@"faultString"];
514         if (reason == nil) reason = name;
515         
516         e = [NSException exceptionWithName:name reason:reason userInfo:nil];
517         [self->params replaceObjectAtIndex:0 withObject:e];
518       }
519     }
520   }
521 }
522
523 /* generic dispatcher */
524
525 - (void)startElement:(NSString *)_localName
526   namespace:(NSString *)_ns
527   rawName:(NSString *)_rawName
528   attributes:(id<SaxAttributes>)_attrs
529 {
530   NSString *tmp = nil;
531   SEL      sel;
532   int      idx;
533   
534   [self->tagStack addObject:_rawName];
535
536   tmp = ((idx = [_attrs indexOfRawName:@"NSObjectClass"]) != NSNotFound)
537       ? [_attrs valueAtIndex:idx]
538       : nil;
539     
540   [self->className autorelease];
541   self->className = [tmp retain];
542
543   if (self->invalidCall) return;
544
545   if ([_rawName isEqualToString:@"dateTime.iso8601"])
546     _rawName = @"dateTime";
547
548   [self->characters setString:@""];
549
550   tmp = [NSString stringWithFormat:@"start_%@:",_rawName];
551   if ((sel = NSSelectorFromString(tmp))) {
552     if ([self respondsToSelector:sel])
553       [self performSelector:sel withObject:_attrs];
554   }
555 }
556
557 - (void)endElement:(NSString *)_localName
558   namespace:(NSString *)_ns
559   rawName:(NSString *)_rawName
560 {
561   unsigned stackDepth, lastIdx;
562   NSString *tmp;
563   SEL sel;
564
565   if (self->nextCharactersProcessor != NULL) {
566     void (*m)(id, SEL, unichar *, int);
567     unichar *chars;
568     unsigned len;
569
570     len   = [self->characters length];
571     chars = malloc(sizeof(unichar)*len);
572     [self->characters getCharacters:chars];
573     
574     if ((m = (void*)[self methodForSelector:self->nextCharactersProcessor]))
575       m(self, self->nextCharactersProcessor, chars, len);
576     
577     free(chars);
578   }
579   
580   self->nextCharactersProcessor = NULL;
581   
582   if ((stackDepth = [self->tagStack count]) == 0) {
583     self->invalidCall = YES;
584     return;
585   }
586   lastIdx = stackDepth - 1;
587   if (![[self->tagStack objectAtIndex:lastIdx] isEqualToString:_rawName]) {
588     self->invalidCall = YES;
589     return;
590   }
591   [self->tagStack removeObjectAtIndex:lastIdx];
592   stackDepth--;
593
594   if (self->invalidCall) {
595     return;
596   }
597   
598   if ([_rawName isEqualToString:@"dateTime.iso8601"])
599     _rawName = @"dateTime";
600   
601   tmp = [NSString stringWithFormat:@"end_%@", _rawName];
602   if ((sel = NSSelectorFromString(tmp))) {
603     if ([self respondsToSelector:sel])
604       [self performSelector:sel];
605   }
606 }
607
608 - (void)characters:(unichar *)_chars length:(int)_len {
609   if (_len > 0) {
610     [self->characters appendString:
611          [NSString stringWithCharacters:_chars length:_len]];
612   }
613 }
614
615 /* errors */
616
617 - (void)warning:(SaxParseException *)_exception {
618   NSLog(@"XML-RPC warning: %@", _exception);
619 }
620 - (void)error:(SaxParseException *)_exception {
621   NSLog(@"XML-RPC error: %@", _exception);
622 }
623
624 @end /* XmlRpcSaxHandler */