]> err.no Git - sope/blob - sope-appserver/NGObjWeb/WebDAV/SaxDAVHandler.m
major improvements in resource/template lookup with SoProduct's
[sope] / sope-appserver / NGObjWeb / WebDAV / SaxDAVHandler.m
1 /*
2   Copyright (C) 2002-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 "SaxDAVHandler.h"
23 #include "EOFetchSpecification+SoDAV.h"
24 #include <SaxObjC/XMLNamespaces.h>
25 #include <EOControl/EOQualifier.h>
26 #include "common.h"
27
28 /*
29   TODO: support parsing of DASL
30   basics are done, open are:
31     sort orderings
32
33   Breaks on SQL searches without Brief: T, which contain a 404 propstat:
34   ---
35   <a:propstat>...found args...</a:propstat>
36   <a:propstat><a:status>HTTP/1.1 404 Resource Not Found</a:status><a:prop><e:namesuffix/><e:telexnumber/><e:ttytddphone/><e:bday/><e:weddinganniversary/><h:x3A1D001E/><h:x3A1A001E/></a:prop></a:propstat>
37   ---
38   
39   a set tag can be either in a "propertyupdate" or in a "response"
40
41   <set><prop><a>1</a></prop><prop><b>2</b></prop>
42   <response><prop><a>1</a><b>2</b></prop></response>
43 */
44
45 @implementation SaxDAVHandler
46
47 static BOOL debugPropValue = NO;
48 static BOOL heavyLog = NO;
49
50 + (void)initialize {
51   static BOOL didInit = NO;
52   if (!didInit) {
53     NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
54     didInit = YES;
55     debugPropValue = [ud boolForKey:@"DAVParserDebugProp"];
56     heavyLog       = [ud boolForKey:@"DAVParserHeavyLog"];
57   }
58 }
59
60 - (void)dealloc {
61   [self reset];
62   [self->locator   release];
63   [self->propNames release];
64   [self->responses release];
65   [self->cdata     release];
66   [super dealloc];
67 }
68
69 /* cleanup */
70
71 - (void)parseReset {
72   /* only reset non-result items */
73   if (heavyLog) [self logWithFormat:@"reset parser state"];
74   self->propValueNesting  = 0;
75   self->in.PropFind       = 0;
76   self->in.Prop           = 0;
77   self->in.Response       = 0;
78   self->in.MultiStatus    = 0;
79   self->in.Href           = 0;
80   self->in.Status         = 0;
81   self->in.PropStat       = 0;
82   self->in.PropertyUpdate = 0;
83   self->in.Set            = 0;
84   self->in.Remove         = 0;
85   self->in.SearchRequest  = 0;
86   self->in.basicsearch    = 0;
87   self->in.select         = 0;
88   self->in.from           = 0;
89   self->in.scope          = 0;
90   self->in.depth          = 0;
91   self->in.where          = 0;
92   self->in.gt             = 0;
93   self->in.lt             = 0;
94   self->in.gte            = 0;
95   self->in.lte            = 0;
96   self->in.eq             = 0;
97   self->in.literal        = 0;
98   self->in.orderby        = 0;
99   self->in.order          = 0;
100   self->in.ascending      = 0;
101   self->in.target         = 0;
102   
103   [self->response          release]; self->response          = nil;
104   [self->cdata             release]; self->cdata             = nil;
105   [self->lastLiteral       release]; self->lastLiteral       = nil;
106   [self->lastScopeHref     release]; self->lastScopeHref     = nil;
107   [self->lastHref          release]; self->lastHref          = nil;
108   [self->lastScopeDepth    release]; self->lastScopeDepth    = nil;
109   [self->lastWherePropName release]; self->lastWherePropName = nil;
110 }
111 - (void)reset {
112   /* reset everything, including result items */
113   if (heavyLog) [self logWithFormat:@"reset"];
114   [self parseReset];
115   self->findAllProps  = NO;
116   self->findPropNames = NO;
117   [self->propNames removeAllObjects];
118   [self->propSet   removeAllObjects];
119   [self->responses removeAllObjects];
120   [self->targets   removeAllObjects];
121   [self->compoundQualStack removeAllObjects];
122   [self->qualifiers release]; self->qualifiers = nil;
123   [self->searchSQL  release]; self->searchSQL  = nil;
124   [self->fspec      release]; self->fspec      = nil;
125 }
126
127 /* accessors */
128
129 - (void)setDelegate:(id)_delegate {
130   self->delegate = _delegate;
131 }
132 - (id)delegate {
133   return self->delegate;
134 }
135
136 /* results */
137
138 - (BOOL)propFindAllProperties {
139   return self->findAllProps;
140 }
141 - (BOOL)propFindPropertyNames {
142   return self->findPropNames;
143 }
144 - (NSArray *)propFindQueriedNames {
145   if ([self->propNames count] == 0) return nil;
146   return [[self->propNames copy] autorelease];
147 }
148
149 - (NSArray *)bpropFindTargets {
150   if ([self->targets count] == 0) return nil;
151   return [[self->targets copy] autorelease];
152 }
153
154 /* proppatch results */
155
156 - (NSArray *)propPatchPropertyNamesToRemove {
157   if ([self->propNames count] == 0) return nil;
158   return [[self->propNames copy] autorelease];
159 }
160 - (NSDictionary *)propPatchValues {
161   if ([self->propSet count] == 0) return nil;
162   return [[self->propSet copy] autorelease];
163 }
164
165 /* search query results */
166
167 - (EOFetchSpecification *)searchFetchSpecification {
168   EOFetchSpecification *fs;
169   
170   if (heavyLog) [self logWithFormat:@"build search fetchspec"];
171   
172   if (self->fspec) {
173     if (heavyLog) [self logWithFormat:@"  use parsed fetchspec"];
174     return self->fspec;
175   }
176   
177   if ([self->searchSQL length] == 0) {
178     if (heavyLog) [self logWithFormat:@"  no SQL to parse"];
179     return nil;
180   }
181   
182   fs = [EOFetchSpecification parseWebDAVSQLString:self->searchSQL];
183   if (fs == nil) {
184     [self logWithFormat:@"could not parse SQL: '%@'", self->searchSQL];
185     return nil;
186   }
187   if (heavyLog) [self logWithFormat:@"  parsed: %@", fs];
188   return fs;
189 }
190
191 /* positioning info */
192
193 - (void)setDocumentLocator:(id<NSObject,SaxLocator>)_locator {
194   ASSIGN(self->locator, _locator);
195 }
196
197 /* parsing */
198
199 - (void)startDocument {
200   if (heavyLog) [self logWithFormat:@"start document"];
201   [self reset];
202 }
203 - (void)endDocument {
204   if (heavyLog) [self logWithFormat:@"end document"];
205   [self parseReset];
206 }
207
208 /* dav elements */
209
210 - (void)startSQLElement {
211   self->in.SQL = 1;
212       
213   if (self->cdata) {
214     [self logWithFormat:@"some cdata collection already in progress ?"];
215   }
216   else
217     self->cdata = [[NSMutableString alloc] initWithCapacity:256];
218 }
219 - (void)endSQLElement {
220   /* TODO: could immediatly parse SQL into fetch-spec */
221   self->in.SQL = 0;
222   [self->searchSQL release]; self->searchSQL = nil;
223   self->searchSQL = [self->cdata copy];
224   [self->cdata release]; self->cdata = nil;
225 }
226
227 - (void)startLiteralElement {
228   self->in.literal = 1;
229   if (self->cdata)
230     [self logWithFormat:@"some cdata collection already in progress ?"];
231   else
232     self->cdata = [[NSMutableString alloc] initWithCapacity:256];
233 }
234 - (void)endLiteralElement {
235   self->in.literal = 0;
236   [self->lastLiteral release]; self->lastLiteral = nil;
237   self->lastLiteral = [self->cdata copy];
238   [self->cdata release]; self->cdata = nil;
239 }
240
241 - (void)startHrefElement {
242   self->in.Href = 1;
243   if (self->in.scope || self->in.target || self->in.Response) {
244     if (self->cdata)
245       [self logWithFormat:@"some cdata collection already in progress ?"];
246     else
247       self->cdata = [[NSMutableString alloc] initWithCapacity:256];
248   }
249 }
250 - (void)endHrefElement {
251   self->in.Href = 0;
252   if (self->in.scope) {
253     [self->lastScopeHref release]; self->lastScopeHref = nil;
254     self->lastScopeHref = [self->cdata copy];
255     [self->cdata release]; self->cdata = nil;
256   }
257   else if (self->in.target && self->cdata != nil) {
258     [self->targets addObject:self->cdata];
259     [self->cdata release]; self->cdata = nil;
260   }
261   else if (self->in.Response && self->cdata != nil) {
262     [self->lastHref release]; self->lastHref = nil;
263     self->lastHref = [self->cdata copy];
264     [self->cdata release]; self->cdata = nil;
265   }
266 }
267
268 - (void)startDepthElement {
269   self->in.depth = 1;
270   if (self->in.scope) {
271     if (self->cdata)
272       [self logWithFormat:@"some cdata collection already in progress ?"];
273     else
274       self->cdata = [[NSMutableString alloc] initWithCapacity:256];
275   }
276 }
277 - (void)endDepthElement {
278   self->in.depth = 0;
279   if (self->in.scope) {
280     [self->lastScopeDepth release]; self->lastScopeDepth = nil;
281     self->lastScopeDepth = [self->cdata copy];
282     [self->cdata release]; self->cdata = nil;
283   }
284 }
285
286 - (void)startBasicSearch {
287   self->in.basicsearch = 1;
288   if (self->fspec)
289     [self logWithFormat:@"basicsearch collection already in progress ?"];
290   else
291     self->fspec = [[EOFetchSpecification alloc] init];
292 }
293
294 - (void)startPropUpdate {
295   self->in.PropertyUpdate = 1;
296   if (self->propNames == nil)
297     self->propNames = [[NSMutableArray alloc] initWithCapacity:64];
298   if (self->propSet == nil)
299     self->propSet = [[NSMutableDictionary alloc] initWithCapacity:64];
300 }
301
302 - (void)startPropSet {
303   self->in.Set = 1;
304   if (self->propSet == nil)
305     self->propSet = [[NSMutableDictionary alloc] initWithCapacity:64];
306   if (debugPropValue)
307     [self logWithFormat:@"start <set> tag ..."];
308 }
309 - (void)endPropSet {
310   self->in.Set = 0;
311   if (debugPropValue) {
312     [self logWithFormat:@"end </set> tag (%i props in set) ...", 
313             [self->propSet count]];
314   }
315 }
316
317 - (void)startProp {
318   if (self->propSet == nil)
319     self->propSet = [[NSMutableDictionary alloc] initWithCapacity:64];
320   
321   self->in.Prop = 1;
322   if (debugPropValue)
323     [self logWithFormat:@"start <prop> tag ..."];
324 }
325 - (void)endProp {
326   self->in.Prop = 0;
327 }
328
329 - (void)startTarget {
330   self->in.target = 1;
331   [self->targets removeAllObjects];
332   if (self->targets == nil) 
333     self->targets = [[NSMutableArray alloc] initWithCapacity:16];
334 }
335
336 - (void)startPropFindSpec:(NSString *)_localName {
337   unsigned len;
338   unichar  c;
339   NSString *fqn;
340   
341   if ((len = [_localName length]) == 0)
342     return;
343   c = [_localName characterAtIndex:0];
344     
345   if (c == 'a' && len == 7) {
346     if ([_localName isEqualToString:@"allprop"]) {
347       self->findAllProps = 1;
348       return;
349     }
350   }
351   if (c == 'p' && len == 8) {
352     if ([_localName isEqualToString:@"propname"]) {
353       self->findPropNames = 1;
354       return;
355     }
356   }
357     
358   fqn = [[NSString alloc] initWithFormat:@"{%@}%@", XMLNS_WEBDAV, _localName];
359   [self->propNames addObject:fqn];
360   [fqn release];
361 }
362
363 - (void)startResponseElement {
364   self->in.Response = 1;
365   
366   if (heavyLog) [self logWithFormat:@"clearing property-set"];
367   [self->propSet removeAllObjects];
368 }
369 - (void)endResponseElement {
370   self->in.Response = 0;
371   
372   if ([self->delegate respondsToSelector:
373              @selector(davHandler:receivedProperties:forURI:)]) {
374     [self->delegate
375          davHandler:self
376          receivedProperties:self->propSet
377          forURI:self->lastHref];
378   }
379   else if (self->response)
380     [self->responses addObject:self->response];
381 }
382
383 - (void)endBasicSearch {
384   /* only works with a single 'from', 'where' and 'select' */
385   NSMutableDictionary *hints;
386   
387   self->in.basicsearch = 0;
388   
389   if (self->lastScopeHref)
390     [self->fspec setEntityName:self->lastScopeHref];
391   
392   // qualifier
393   
394   hints = [[NSMutableDictionary alloc] initWithCapacity:8];
395   
396   if (self->lastScopeDepth) {
397     if ([self->lastScopeDepth isEqualToString:@"infinity"])
398       [hints setObject:@"deep" forKey:@"scope"];
399     else if ([self->lastScopeDepth isEqualToString:@"1"])
400       [hints setObject:@"flat+self" forKey:@"scope"];
401     else if ([self->lastScopeDepth isEqualToString:@"1,noroot"])
402       [hints setObject:@"flat" forKey:@"scope"];
403     else if ([self->lastScopeDepth isEqualToString:@"0"])
404       [hints setObject:@"self" forKey:@"scope"];
405     else {
406       [self logWithFormat:@"unknown search depth '%@'", self->lastScopeDepth];
407       [hints setObject:self->lastScopeDepth forKey:@"scope"];
408     }
409   }
410   
411   if (self->findPropNames)
412     [hints setObject:[NSNumber numberWithBool:YES] forKey:@"namesOnly"];
413   if (self->propNames)
414     [hints setObject:self->propNames forKey:@"attributes"];
415   /* Note: "allprops" is "no attributes set" in fspec */
416   
417   [self->fspec setHints:hints];
418   [hints release];
419   
420   // [self logWithFormat:@"parsed spec: %@", fspec];
421 }
422
423 - (void)addParsedQualifier:(EOQualifier *)_qualifier {
424   if (self->qualifiers)
425     [self->qualifiers addObject:_qualifier];
426   else {
427     //[self logWithFormat:@"got root qualifier: %@", _qualifier];
428     [self->fspec setQualifier:_qualifier];
429   }
430 }
431
432 - (void)endComparisonQualifier:(SEL)_op {
433   /* 
434      collect:
435      <D:eq>
436        <D:prop><D:sn/></D:prop>
437        <D:literal>Mueller</D:literal>
438      </D:eq>
439   */
440   EOKeyValueQualifier *kv;
441   
442   kv = [[EOKeyValueQualifier alloc] initWithKey:self->lastWherePropName
443                                     operatorSelector:_op
444                                     value:self->lastLiteral];
445   [self addParsedQualifier:kv];
446   [kv release];
447   
448   [self->lastWherePropName release]; self->lastWherePropName = nil;
449   [self->lastLiteral       release]; self->lastLiteral = nil;
450 }
451
452 - (void)beginCompoundQualifier {
453   if (self->compoundQualStack == nil)
454     self->compoundQualStack = [[NSMutableArray alloc] initWithCapacity:16];
455   
456   self->qualifiers = [[NSMutableArray alloc] initWithCapacity:4];
457   [self->compoundQualStack addObject:self->qualifiers];
458 }
459 - (void)endCompoundQualifier:(NSString *)_localName {
460   EOQualifier *q;
461   unsigned stackSize;
462   
463   if ([_localName isEqualToString:@"not"]) {
464     unsigned cnt;
465     
466     if ((cnt = [self->qualifiers count]) == 0)
467       q = nil;
468     else {
469       q = [self->qualifiers objectAtIndex:0];
470       if (cnt != 1) {
471         [self warnWithFormat:@"too many subqualifiers in not !: %@",
472                 self->qualifiers];
473       }
474     }
475     q = [[EONotQualifier alloc] initWithQualifier:q];
476   }
477   else {
478     Class qc = Nil;
479     
480     if ([_localName isEqualToString:@"and"])
481       qc = [EOAndQualifier class];
482     else if ([_localName isEqualToString:@"or"])
483       qc = [EOOrQualifier class];
484     else {
485       [self logWithFormat:@"unknown compound qualifier: '%@'", _localName];
486       qc = Nil;
487     }
488     
489     q = [[qc alloc] initWithQualifierArray:self->qualifiers];
490   }
491   [self->qualifiers release]; self->qualifiers = nil;
492   
493   if ((stackSize = [self->compoundQualStack count]) == 0) {
494     [self errorWithFormat:@"the qualifier stack is mixed up !"];
495   }
496   else if (stackSize > 1) {
497     /* this one was not the root qualifier */
498     [self->compoundQualStack removeObjectAtIndex:(stackSize - 1)];
499     self->qualifiers = [self->compoundQualStack objectAtIndex:(stackSize -2 )];
500   }
501   else
502     /* this one was the root qualifier */
503     [self->compoundQualStack removeObjectAtIndex:0];
504   
505   if (q) [self addParsedQualifier:q];
506   [q release];
507 }
508
509 - (void)startPropValueElement:(NSString *)_localName namespace:(NSString *)_ns {
510   self->propValueNesting++;
511   if (debugPropValue) {
512     [self debugWithFormat:@"start[%i]: {%@}%@", self->propValueNesting,
513             _ns, _localName];
514   }
515       
516   if (self->propValueNesting == 1) {
517     /* starting value */
518         
519     if (self->cdata) {
520       /* this can happen with nested tags ! */
521       [self logWithFormat:@"some cdata collection already in progress ?"];
522     }
523     else
524       self->cdata = [[NSMutableString alloc] initWithCapacity:256];
525   }
526   else {
527     /* add tag to value for later parsing */
528     [self->cdata appendString:@"<"];
529     [self->cdata appendString:@"V:"];
530     [self->cdata appendString:_localName];
531     [self->cdata appendString:@" xmlns:V=\""];
532     [self->cdata appendString:_ns];
533     [self->cdata appendString:@"\""];
534     [self->cdata appendString:@">"];
535   }
536 }
537 - (void)endPropValueElement:(NSString *)_localName namespace:(NSString *)_ns {
538   NSString *t;
539   NSString *fqn;
540   
541   if (debugPropValue) {
542     [self debugWithFormat:@"end[%i]: {%@}%@", self->propValueNesting,
543             _ns, _localName];
544   }
545   
546   if (self->propValueNesting == 1) {
547     fqn = [NSString stringWithFormat:@"{%@}%@", _ns, _localName];
548       
549     t = [self->cdata copy];
550     [self->cdata release]; self->cdata = nil;
551         
552     if (t) 
553       [self->propSet setObject:t forKey:fqn];
554     else {
555       [self->propSet setObject:[NSNull null] forKey:fqn];
556       [self errorWithFormat:@"lost the parsing cdata (broken nesting) ?!"];
557     }
558     [t release];
559   }
560   else {
561     /* add tag to value for later parsing */
562     [self->cdata appendString:@"</"];
563     [self->cdata appendString:@"V:"];
564     [self->cdata appendString:_localName];
565     [self->cdata appendString:@">"];
566   }
567   
568   self->propValueNesting--;
569 }
570
571 /* dav element dispatcher */
572
573 - (void)startDavElement:(NSString *)_localName {
574   unsigned len;
575   unichar c;
576
577   if ((len = [_localName length]) == 0)
578     return;
579   
580   if ((self->in.PropFind || self->in.select) && self->in.Prop) {
581     [self startPropFindSpec:_localName];
582     return;
583   }
584   if (self->in.where && self->in.Prop) {
585     /* a property in a where */
586     NSString *fqn;
587     fqn = [NSString stringWithFormat:@"{%@}%@", XMLNS_WEBDAV, _localName];
588     ASSIGNCOPY(self->lastWherePropName, fqn);
589     /* TODO: should return ? */
590   }
591   if (self->in.PropertyUpdate && self->in.Set && self->in.Prop) {
592     [self startPropValueElement:_localName namespace:XMLNS_WEBDAV];
593     return;
594   }
595   if (self->in.Response && self->in.PropStat && self->in.Prop) {
596     [self startPropValueElement:_localName namespace:XMLNS_WEBDAV];
597     return;
598   }
599   
600   c = [_localName characterAtIndex:0];
601   switch (c) {
602   case 'a':
603     if ([_localName isEqualToString:@"allprop"])
604       self->findAllProps = 1;
605     else if ([_localName isEqualToString:@"ascending"])
606       self->ascending = YES;
607     else if ([_localName isEqualToString:@"and"])
608       [self beginCompoundQualifier];
609     break;
610     
611   case 'b':
612     if ([_localName isEqualToString:@"basicsearch"])
613       [self startBasicSearch];
614     break;
615     
616   case 'd':
617     if ([_localName isEqualToString:@"depth"])
618       [self startDepthElement];
619     break;
620
621   case 'e':
622     if ([_localName isEqualToString:@"eq"])
623       self->in.eq = 1;
624     break;
625     
626   case 'f':
627     if ([_localName isEqualToString:@"from"])
628       self->in.from = 1;
629     break;
630     
631   case 'g':
632     if ([_localName isEqualToString:@"gt"])
633       self->in.gt = 1;
634     else if ([_localName isEqualToString:@"gte"])
635       self->in.gte = 1;
636     break;
637     
638   case 'h':
639     if ([_localName isEqualToString:@"href"])
640       [self startHrefElement];
641     break;
642     
643   case 'l':
644     if ([_localName isEqualToString:@"lt"])
645       self->in.lt = 1;
646     else if ([_localName isEqualToString:@"lte"])
647       self->in.lte = 1;
648     else if ([_localName isEqualToString:@"literal"])
649       [self startLiteralElement];
650     break;
651
652   case 'm':
653     if ([_localName isEqualToString:@"multistatus"]) {
654       self->in.MultiStatus = 1;
655       if (self->responses == nil)
656         self->responses = [[NSMutableArray alloc] initWithCapacity:64];
657     }
658     break;
659
660   case 'n':
661     if ([_localName isEqualToString:@"not"])
662       [self beginCompoundQualifier];
663     break;
664
665   case 'o':
666     if ([_localName isEqualToString:@"order"])
667       self->in.order = 1;
668     else if ([_localName isEqualToString:@"orderby"])
669       self->in.orderby = 1;
670     else if ([_localName isEqualToString:@"or"])
671       [self beginCompoundQualifier];
672     break;
673     
674   case 'p':
675     if ([_localName isEqualToString:@"propfind"]) {
676       if (self->propNames == nil)
677         self->propNames = [[NSMutableArray alloc] initWithCapacity:64];
678       self->in.PropFind = 1;
679     }
680     else if ([_localName isEqualToString:@"prop"])
681       [self startProp];
682     else if ([_localName isEqualToString:@"propstat"])
683       self->in.PropStat = 1;
684     else if ([_localName isEqualToString:@"propertyupdate"])
685       [self startPropUpdate];
686     else if ([_localName isEqualToString:@"propname"]) {
687       self->findPropNames = 1;
688     }
689     break;
690
691   case 'r':
692     if ([_localName isEqualToString:@"response"])
693       [self startResponseElement];
694     else if ([_localName isEqualToString:@"remove"])
695       self->in.Remove = 1;
696     break;
697
698   case 's':
699     if ([_localName isEqualToString:@"status"])
700       self->in.Status = 1;
701     else if ([_localName isEqualToString:@"set"])
702       [self startPropSet];
703     else if ([_localName isEqualToString:@"select"])
704       self->in.select = 1;
705     else if ([_localName isEqualToString:@"scope"])
706       self->in.scope = 1;
707     else if ([_localName isEqualToString:@"searchrequest"])
708       self->in.SearchRequest = 1;
709     else if ([_localName isEqualToString:@"sql"])
710       [self startSQLElement];
711     break;
712     
713   case 't':
714     if ([_localName isEqualToString:@"target"])
715       [self startTarget];
716     break;
717     
718   case 'w':
719     if ([_localName isEqualToString:@"where"])
720       self->in.where = 1;
721     break;
722     
723   default:
724     break;
725   }
726 }
727 - (void)endDavElement:(NSString *)_localName {
728   unsigned len;
729   unichar c;
730   
731   if ((len = [_localName length]) == 0)
732     return;
733   c = [_localName characterAtIndex:0];
734   
735   if (self->in.PropertyUpdate && self->in.Set && self->in.Prop) {
736     if (![_localName isEqualToString:@"prop"]) {
737       [self endPropValueElement:_localName namespace:XMLNS_WEBDAV];
738       return;
739     }
740   }
741   if (self->in.Response && self->in.PropStat && self->in.Prop) {
742     if (![_localName isEqualToString:@"prop"]) {
743       [self endPropValueElement:_localName namespace:XMLNS_WEBDAV];
744       return;
745     }
746   }
747   
748   switch (c) {
749   case 'a':
750     if ([_localName isEqualToString:@"and"])
751       [self endCompoundQualifier:@"and"];
752     break;
753   case 'b':
754     if ([_localName isEqualToString:@"basicsearch"])
755       [self endBasicSearch];
756     break;
757     
758   case 'd':
759     if ([_localName isEqualToString:@"depth"])
760       [self endDepthElement];
761     break;
762     
763   case 'e':
764     if ([_localName isEqualToString:@"eq"]) {
765       [self endComparisonQualifier:EOQualifierOperatorEqual];
766       self->in.eq = 0;
767     }
768     break;
769     
770   case 'f':
771     if ([_localName isEqualToString:@"from"])
772       self->in.from = 0;
773     break;
774
775   case 'g':
776     if ([_localName isEqualToString:@"gt"]) {
777       [self endComparisonQualifier:EOQualifierOperatorGreaterThan];
778       self->in.gt = 0;
779     }
780     else if ([_localName isEqualToString:@"gte"]) {
781       [self endComparisonQualifier:EOQualifierOperatorGreaterThanOrEqualTo];
782       self->in.gte = 0;
783     }
784     break;
785     
786   case 'h':
787     if ([_localName isEqualToString:@"href"])
788       [self endHrefElement];
789     break;
790     
791   case 'l':
792     if ([_localName isEqualToString:@"lt"]) {
793       [self endComparisonQualifier:EOQualifierOperatorLessThan];
794       self->in.lt = 0;
795     }
796     else if ([_localName isEqualToString:@"lte"]) {
797       [self endComparisonQualifier:EOQualifierOperatorLessThanOrEqualTo];
798       self->in.lte = 0;
799     }
800     else if ([_localName isEqualToString:@"literal"])
801       [self endLiteralElement];
802     break;
803     
804   case 'm':
805     if ([_localName isEqualToString:@"multistatus"])
806       self->in.MultiStatus = 0;
807     break;
808     
809   case 'n':
810     if ([_localName isEqualToString:@"not"])
811       [self endCompoundQualifier:@"not"];
812     break;
813
814   case 'o':
815     if ([_localName isEqualToString:@"order"])
816       self->in.order = 0;
817     else if ([_localName isEqualToString:@"orderby"])
818       self->in.orderby = 0;
819     else if ([_localName isEqualToString:@"or"])
820       [self endCompoundQualifier:@"or"];
821     break;
822     
823   case 'p':
824     if ([_localName isEqualToString:@"propfind"])
825       self->in.PropFind = 0;
826     else if ([_localName isEqualToString:@"prop"])
827       [self endProp];
828     else if ([_localName isEqualToString:@"propstat"])
829       self->in.PropStat = 0;
830     else if ([_localName isEqualToString:@"propertyupdate"])
831       self->in.PropertyUpdate = 0;
832     break;
833
834   case 'r':
835     if ([_localName isEqualToString:@"response"])
836       [self endResponseElement];
837     else if ([_localName isEqualToString:@"remove"])
838       self->in.Remove = 0;
839     break;
840
841   case 's':
842     if ([_localName isEqualToString:@"set"])
843       [self endPropSet];
844     else if ([_localName isEqualToString:@"select"])
845       self->in.select = 0;
846     else if ([_localName isEqualToString:@"scope"])
847       self->in.scope = 0;
848     else if ([_localName isEqualToString:@"searchrequest"])
849       self->in.SearchRequest = 0;
850     
851     else if ([_localName isEqualToString:@"sql"])
852       [self endSQLElement];
853     else if ([_localName isEqualToString:@"status"])
854       self->in.Status = 0;
855     break;
856
857   case 't':
858     if ([_localName isEqualToString:@"target"])
859       self->in.target = 0;
860     break;
861
862   case 'w':
863     if ([_localName isEqualToString:@"where"])
864       self->in.where = 0;
865     break;
866   }
867 }
868
869 /* element callbacks */
870
871 - (void)startElement:(NSString *)_localName
872   namespace:(NSString *)_ns
873   rawName:(NSString *)_rawName
874   attributes:(id<SaxAttributes>)_attributes
875 {
876   if (heavyLog) [self logWithFormat:@"START {%@}%@", _ns, _localName];
877   
878   if ([_ns isEqualToString:XMLNS_WEBDAV]) {
879     [self startDavElement:_localName];
880   }
881   else if (self->in.PropFind || self->in.select) {
882     NSString *fqn;
883     fqn = [NSString stringWithFormat:@"{%@}%@", _ns, _localName];
884     [self->propNames addObject:fqn];
885   }
886   else if (self->in.where && self->in.Prop) {
887     NSString *fqn;
888     fqn = [NSString stringWithFormat:@"{%@}%@", _ns, _localName];
889     ASSIGNCOPY(self->lastWherePropName, fqn);
890   }
891   else if (self->in.PropertyUpdate) {
892     if (self->in.Set) {
893       [self startPropValueElement:_localName namespace:_ns];
894     }
895     else if (self->in.Remove) {
896       NSString *fqn;
897     
898       fqn = [NSString stringWithFormat:@"{%@}%@", _ns, _localName];
899       [self->propNames addObject:fqn];
900     }
901   }
902   else if (self->in.Response && self->in.PropStat && self->in.Prop)
903     [self startPropValueElement:_localName namespace:_ns];
904 }
905
906 - (void)endElement:(NSString *)_localName
907   namespace:(NSString *)_ns
908   rawName:(NSString *)_rawName
909 {
910   if (heavyLog) [self logWithFormat:@"END {%@}%@", _ns, _localName];
911   
912   if ([_ns isEqualToString:XMLNS_WEBDAV]) {
913     [self endDavElement:_localName];
914   }
915   else if (self->in.PropFind) {
916     /* no close tags in propfind ... */
917   }
918   else if (self->in.PropertyUpdate) {
919     if (self->in.Set)
920       [self endPropValueElement:_localName namespace:_ns];
921     else if (self->in.Remove) {
922       /* no close tags in remove section ... */
923     }
924   }
925   else if (self->in.Response && self->in.PropStat && self->in.Prop)
926     [self endPropValueElement:_localName namespace:_ns];
927 }
928
929 /* CDATA */
930
931 - (void)characters:(unichar *)_chars length:(int)_len {
932   if (heavyLog) [self logWithFormat:@"got %i chars", _len];
933   
934   if (_len > 0 && (self->cdata != nil)) {
935     NSString *s;
936     
937     s = [[NSString alloc] initWithCharacters:_chars length:_len];
938     [self->cdata appendString:s];
939     [s release];
940   }
941 }
942
943 @end /* SaxDAVHandler */