]> err.no Git - sope/blob - sope-xml/XmlRpc/XmlRpcSaxHandler.m
Add libxml2-dev to libsope-xml4.7-dev deps
[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 = [(XmlRpcValue *)[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     ? (id)[_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 (![[(XmlRpcValue *)[self->valueStack lastObject] value] 
334          isKindOfClass:DictionaryClass]) {
335     self->invalidCall = YES;
336   }
337   else {
338     if (self->memberNameStack == nil)
339       self->memberNameStack = [[NSMutableArray alloc] initWithCapacity:8];
340     if (self->memberValueStack == nil)
341       self->memberValueStack = [[NSMutableArray alloc] initWithCapacity:8];
342   }
343   self->nextCharactersProcessor = NULL;
344 }
345
346 - (void)end_member {
347   id tmp; // TODO: can't we type the var?
348
349   tmp = [(XmlRpcValue *)[self->valueStack lastObject] value];
350
351   if ([self->memberNameStack count] != [self->memberValueStack count]) {
352     NSLog(@"Warning(%s): memberNameStack.count != memberValueStack.count"
353           @" (%@ <--> %@)",
354           __PRETTY_FUNCTION__,
355           self->memberNameStack,
356           self->memberValueStack,
357           nil);
358     [self->memberValueStack release]; self->memberValueStack = nil;
359     [self->memberNameStack  release]; self->memberNameStack  = nil;
360     self->invalidCall = YES;
361   }
362   else if ([self->memberNameStack count] == 0) {
363     NSLog(@"Warning(%s): memberNameStack and memberValueStack are empty!",
364           __PRETTY_FUNCTION__,
365           nil);
366     [self->memberValueStack release]; self->memberValueStack = nil;
367     [self->memberNameStack  release]; self->memberNameStack  = nil;
368     self->invalidCall = YES;
369   }
370   else if (![tmp isKindOfClass:DictionaryClass])
371     self->invalidCall = YES;
372   else {
373     [(NSMutableDictionary *)tmp
374                             setObject:[self->memberValueStack lastObject]
375                             forKey:[self->memberNameStack lastObject]];
376     
377     [self->memberNameStack  removeLastObject];
378     [self->memberValueStack removeLastObject];
379   }
380 }
381
382 - (void)start_methodCall:(id<SaxAttributes>)_attrs {
383   /* can't create call here, args unknown !!! */
384   if (self->call != nil) {
385     if (doDebug) 
386       NSLog(@"%s: method-call already setup!", __PRETTY_FUNCTION__);
387     self->invalidCall = YES;
388     return;
389   }
390   if (doDebug) NSLog(@"%s: ...", __PRETTY_FUNCTION__);
391 }
392 - (void)end_methodCall {
393   if (self->call != nil) {
394     if (doDebug) 
395       NSLog(@"%s: method-call already setup!", __PRETTY_FUNCTION__);
396     self->invalidCall = YES;
397     return;
398   }
399   
400   self->call = [[XmlRpcMethodCall alloc] initWithMethodName:self->methodName
401                                          parameters:self->params];
402   
403   /* reset args */
404   [self->methodName release]; self->methodName = nil;
405   [self->params     release]; self->params     = nil;
406 }
407
408 - (void)start_methodResponse:(id<SaxAttributes>)_attrs {
409   if (self->response != nil) {
410     if (doDebug) 
411       NSLog(@"%s: method-response already setup!", __PRETTY_FUNCTION__);
412     self->invalidCall = YES;
413     return;
414   }
415 }
416
417 - (void)end_methodResponse {
418   if (doDebug) NSLog(@"%s: begin ...", __PRETTY_FUNCTION__);
419   
420   if (self->response != nil) {
421     if (doDebug) 
422       NSLog(@"%s:   method-response already setup!", __PRETTY_FUNCTION__);
423     self->invalidCall = YES;
424     return;
425   }
426
427   if ([self->params count] > 1) {
428     if (doDebug) {
429       NSLog(@"%s:   has more than one params (%i)!", __PRETTY_FUNCTION__, 
430       [self->params count]);
431     }
432     self->invalidCall = YES;
433   }
434   
435   if (self->invalidCall) {
436     NSException *error;
437
438     if (doDebug)
439       NSLog(@"%s:   response marked as invalid!", __PRETTY_FUNCTION__);
440     
441     error = [NSException exceptionWithName:@"error while parsing response"
442                          reason:@"error while parsing response"
443                          userInfo:nil];
444     
445     [self->params release];
446     self->params = [[NSMutableArray arrayWithObject:error] retain];
447   }
448
449   self->response =
450     [[XmlRpcMethodResponse alloc] initWithResult:[self->params lastObject]];
451   if (doDebug)
452     NSLog(@"%s:   response: %@", __PRETTY_FUNCTION__, self->response);
453
454   /* reset args */
455   [self->params release]; self->params = nil;
456   if (doDebug) NSLog(@"%s: done.", __PRETTY_FUNCTION__);
457 }
458
459 - (void)start_methodName:(id<SaxAttributes>)_attrs {
460   if (self->call != nil) {
461     self->invalidCall = YES;
462     return;
463   }
464   self->nextCharactersProcessor = @selector(_methodName:length:);
465 }
466
467 - (void)end_methodName {
468   self->nextCharactersProcessor = NULL;
469 }
470 - (void)_methodName:(unichar *)_chars length:(int)_len {
471   [self->methodName release];
472   self->methodName = [[NSString alloc] initWithCharacters:_chars length:_len];
473 }
474
475 - (void)start_fault:(id<SaxAttributes>)_attrs {
476   if (self->params) {
477     self->invalidCall = YES;
478     return;
479   }
480   else
481     self->params = [[NSMutableArray alloc] initWithCapacity:2];
482   
483   self->nextCharactersProcessor = NULL;
484 }
485 - (void)end_fault {
486   if (self->params == nil)
487     self->invalidCall = YES;
488
489   self->nextCharactersProcessor = NULL;
490
491   /* fixup result class */
492   if ([self->params count] != 1) {
493     NSLog(@"XML-RPC: incorrect params count (should be 1 for faults) ?: %@",
494           self->params);
495   }
496   else {
497     XmlRpcValue *fault;
498     
499     fault = [self->params objectAtIndex:0];
500     if (![fault isException]) {
501       if ([fault isDictionary]) {
502         [fault setClassName:@"NSException"];
503       }
504       else {
505         NSException *e;
506         NSString *name;
507         NSString *reason;
508         
509         NSLog(@"XML-RPC: got incorrect fault object (class=%@) ?: %@",
510               [fault className], fault);
511         name = [NSString stringWithFormat:@"XmlRpcFault<%@>", 
512                            [fault valueForKey:@"faultCode"]];
513         if (name == nil) name = @"GenericXmlRpcFault";
514         reason = [fault valueForKey:@"faultString"];
515         if (reason == nil) reason = name;
516         
517         e = [NSException exceptionWithName:name reason:reason userInfo:nil];
518         [self->params replaceObjectAtIndex:0 withObject:e];
519       }
520     }
521   }
522 }
523
524 /* generic dispatcher */
525
526 - (void)startElement:(NSString *)_localName
527   namespace:(NSString *)_ns
528   rawName:(NSString *)_rawName
529   attributes:(id<SaxAttributes>)_attrs
530 {
531   NSString *tmp = nil;
532   SEL      sel;
533   int      idx;
534   
535   [self->tagStack addObject:_rawName];
536
537   tmp = ((idx = [_attrs indexOfRawName:@"NSObjectClass"]) != NSNotFound)
538     ? (id)[_attrs valueAtIndex:idx]
539     : nil;
540     
541   [self->className autorelease];
542   self->className = [tmp retain];
543
544   if (self->invalidCall) return;
545
546   if ([_rawName isEqualToString:@"dateTime.iso8601"])
547     _rawName = @"dateTime";
548
549   [self->characters setString:@""];
550
551   tmp = [NSString stringWithFormat:@"start_%@:",_rawName];
552   if ((sel = NSSelectorFromString(tmp))) {
553     if ([self respondsToSelector:sel])
554       [self performSelector:sel withObject:_attrs];
555   }
556 }
557
558 - (void)endElement:(NSString *)_localName
559   namespace:(NSString *)_ns
560   rawName:(NSString *)_rawName
561 {
562   unsigned stackDepth, lastIdx;
563   NSString *tmp;
564   SEL sel;
565
566   if (self->nextCharactersProcessor != NULL) {
567     void (*m)(id, SEL, unichar *, int);
568     unichar *chars;
569     unsigned len;
570
571     len   = [self->characters length];
572     chars = malloc(sizeof(unichar)*len);
573     [self->characters getCharacters:chars];
574     
575     if ((m = (void*)[self methodForSelector:self->nextCharactersProcessor]))
576       m(self, self->nextCharactersProcessor, chars, len);
577     
578     free(chars);
579   }
580   
581   self->nextCharactersProcessor = NULL;
582   
583   if ((stackDepth = [self->tagStack count]) == 0) {
584     self->invalidCall = YES;
585     return;
586   }
587   lastIdx = stackDepth - 1;
588   if (![[self->tagStack objectAtIndex:lastIdx] isEqualToString:_rawName]) {
589     self->invalidCall = YES;
590     return;
591   }
592   [self->tagStack removeObjectAtIndex:lastIdx];
593   stackDepth--;
594
595   if (self->invalidCall) {
596     return;
597   }
598   
599   if ([_rawName isEqualToString:@"dateTime.iso8601"])
600     _rawName = @"dateTime";
601   
602   tmp = [NSString stringWithFormat:@"end_%@", _rawName];
603   if ((sel = NSSelectorFromString(tmp))) {
604     if ([self respondsToSelector:sel])
605       [self performSelector:sel];
606   }
607 }
608
609 - (void)characters:(unichar *)_chars length:(int)_len {
610   if (_len > 0) {
611     [self->characters appendString:
612          [NSString stringWithCharacters:_chars length:_len]];
613   }
614 }
615
616 /* errors */
617
618 - (void)warning:(SaxParseException *)_exception {
619   NSLog(@"XML-RPC warning: %@", _exception);
620 }
621 - (void)error:(SaxParseException *)_exception {
622   NSLog(@"XML-RPC error: %@", _exception);
623 }
624
625 @end /* XmlRpcSaxHandler */