]> err.no Git - sope/blob - sope-appserver/NGObjWeb/WebDAV/SaxDAVHandler.m
renamed packages as discussed in the developer list
[sope] / sope-appserver / NGObjWeb / WebDAV / SaxDAVHandler.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 "SaxDAVHandler.h"
24 #include "EOFetchSpecification+SoDAV.h"
25 #include <SaxObjC/XMLNamespaces.h>
26 #include <EOControl/EOQualifier.h>
27 #include "common.h"
28
29 /*
30   TODO: support parsing of DASL
31   basics are done, open are:
32     sort orderings
33
34   Breaks on SQL searches without Brief: T, which contain a 404 propstat:
35   ---
36   <a:propstat>...found args...</a:propstat>
37   <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>
38   ---
39   
40   a set tag can be either in a "propertyupdate" or in a "response"
41
42   <set><prop><a>1</a></prop><prop><b>2</b></prop>
43   <response><prop><a>1</a><b>2</b></prop></response>
44 */
45
46 @implementation SaxDAVHandler
47
48 static BOOL debugPropValue = NO;
49 static BOOL heavyLog = NO;
50
51 + (void)initialize {
52   static BOOL didInit = NO;
53   if (!didInit) {
54     NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
55     didInit = YES;
56     debugPropValue = [ud boolForKey:@"DAVParserDebugProp"];
57     heavyLog       = [ud boolForKey:@"DAVParserHeavyLog"];
58   }
59 }
60
61 - (void)dealloc {
62   [self reset];
63   [self->locator   release];
64   [self->propNames release];
65   [self->responses release];
66   [self->cdata     release];
67   [super dealloc];
68 }
69
70 /* cleanup */
71
72 - (void)parseReset {
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   if (heavyLog) [self logWithFormat:@"reset"];
113   [self parseReset];
114   self->findAllProps  = NO;
115   self->findPropNames = NO;
116   [self->propNames removeAllObjects];
117   [self->propSet   removeAllObjects];
118   [self->responses removeAllObjects];
119   [self->targets   removeAllObjects];
120   [self->compoundQualStack removeAllObjects];
121   [self->qualifiers release]; self->qualifiers = nil;
122   [self->searchSQL  release]; self->searchSQL  = nil;
123   [self->fspec      release]; self->fspec      = nil;
124 }
125
126 /* accessors */
127
128 - (void)setDelegate:(id)_delegate {
129   self->delegate = _delegate;
130 }
131 - (id)delegate {
132   return self->delegate;
133 }
134
135 /* results */
136
137 - (BOOL)propFindAllProperties {
138   return self->findAllProps;
139 }
140 - (BOOL)propFindPropertyNames {
141   return self->findPropNames;
142 }
143 - (NSArray *)propFindQueriedNames {
144   if ([self->propNames count] == 0) return nil;
145   return [[self->propNames copy] autorelease];
146 }
147
148 - (NSArray *)bpropFindTargets {
149   if ([self->targets count] == 0) return nil;
150   return [[self->targets copy] autorelease];
151 }
152
153 /* proppatch results */
154
155 - (NSArray *)propPatchPropertyNamesToRemove {
156   if ([self->propNames count] == 0) return nil;
157   return [[self->propNames copy] autorelease];
158 }
159 - (NSDictionary *)propPatchValues {
160   if ([self->propSet count] == 0) return nil;
161   return [[self->propSet copy] autorelease];
162 }
163
164 /* search query results */
165
166 - (EOFetchSpecification *)searchFetchSpecification {
167   EOFetchSpecification *fs;
168   
169   if (heavyLog) [self logWithFormat:@"build search fetchspec"];
170   
171   if (self->fspec) {
172     if (heavyLog) [self logWithFormat:@"  use parsed fetchspec"];
173     return self->fspec;
174   }
175   
176   if ([self->searchSQL length] == 0) {
177     if (heavyLog) [self logWithFormat:@"  no SQL to parse"];
178     return nil;
179   }
180   
181   fs = [EOFetchSpecification parseWebDAVSQLString:self->searchSQL];
182   if (fs == nil) {
183     [self logWithFormat:@"could not parse SQL: '%@'", self->searchSQL];
184     return nil;
185   }
186   if (heavyLog) [self logWithFormat:@"  parsed: %@", fs];
187   return fs;
188 }
189
190 /* positioning info */
191
192 - (void)setDocumentLocator:(id<NSObject,SaxLocator>)_locator {
193   ASSIGN(self->locator, _locator);
194 }
195
196 /* parsing */
197
198 - (void)startDocument {
199   if (heavyLog) [self logWithFormat:@"start document"];
200   [self reset];
201 }
202 - (void)endDocument {
203   if (heavyLog) [self logWithFormat:@"end document"];
204   [self parseReset];
205 }
206
207 /* dav elements */
208
209 - (void)startSQLElement {
210   self->in.SQL = 1;
211       
212   if (self->cdata) {
213     [self logWithFormat:@"some cdata collection already in progress ?"];
214   }
215   else
216     self->cdata = [[NSMutableString alloc] initWithCapacity:256];
217 }
218 - (void)endSQLElement {
219   /* TODO: could immediatly parse SQL into fetch-spec */
220   self->in.SQL = 0;
221   [self->searchSQL release]; self->searchSQL = nil;
222   self->searchSQL = [self->cdata copy];
223   [self->cdata release]; self->cdata = nil;
224 }
225
226 - (void)startLiteralElement {
227   self->in.literal = 1;
228   if (self->cdata)
229     [self logWithFormat:@"some cdata collection already in progress ?"];
230   else
231     self->cdata = [[NSMutableString alloc] initWithCapacity:256];
232 }
233 - (void)endLiteralElement {
234   self->in.literal = 0;
235   [self->lastLiteral release]; self->lastLiteral = nil;
236   self->lastLiteral = [self->cdata copy];
237   [self->cdata release]; self->cdata = nil;
238 }
239
240 - (void)startHrefElement {
241   self->in.Href = 1;
242   if (self->in.scope || self->in.target || self->in.Response) {
243     if (self->cdata)
244       [self logWithFormat:@"some cdata collection already in progress ?"];
245     else
246       self->cdata = [[NSMutableString alloc] initWithCapacity:256];
247   }
248 }
249 - (void)endHrefElement {
250   self->in.Href = 0;
251   if (self->in.scope) {
252     [self->lastScopeHref release]; self->lastScopeHref = nil;
253     self->lastScopeHref = [self->cdata copy];
254     [self->cdata release]; self->cdata = nil;
255   }
256   else if (self->in.target && self->cdata != nil) {
257     [self->targets addObject:self->cdata];
258     [self->cdata release]; self->cdata = nil;
259   }
260   else if (self->in.Response && self->cdata != nil) {
261     [self->lastHref release]; self->lastHref = nil;
262     self->lastHref = [self->cdata copy];
263     [self->cdata release]; self->cdata = nil;
264   }
265 }
266
267 - (void)startDepthElement {
268   self->in.depth = 1;
269   if (self->in.scope) {
270     if (self->cdata)
271       [self logWithFormat:@"some cdata collection already in progress ?"];
272     else
273       self->cdata = [[NSMutableString alloc] initWithCapacity:256];
274   }
275 }
276 - (void)endDepthElement {
277   self->in.depth = 0;
278   if (self->in.scope) {
279     [self->lastScopeDepth release]; self->lastScopeDepth = nil;
280     self->lastScopeDepth = [self->cdata copy];
281     [self->cdata release]; self->cdata = nil;
282   }
283 }
284
285 - (void)startBasicSearch {
286   self->in.basicsearch = 1;
287   if (self->fspec)
288     [self logWithFormat:@"basicsearch collection already in progress ?"];
289   else
290     self->fspec = [[EOFetchSpecification alloc] init];
291 }
292
293 - (void)startPropUpdate {
294   self->in.PropertyUpdate = 1;
295   if (self->propNames == nil)
296     self->propNames = [[NSMutableArray alloc] initWithCapacity:64];
297   if (self->propSet == nil)
298     self->propSet = [[NSMutableDictionary alloc] initWithCapacity:64];
299 }
300
301 - (void)startPropSet {
302   self->in.Set = 1;
303   if (self->propSet == nil)
304     self->propSet = [[NSMutableDictionary alloc] initWithCapacity:64];
305   if (debugPropValue)
306     [self logWithFormat:@"start <set> tag ..."];
307 }
308 - (void)endPropSet {
309   self->in.Set = 0;
310   if (debugPropValue) {
311     [self logWithFormat:@"end </set> tag (%i props in set) ...", 
312             [self->propSet count]];
313   }
314 }
315
316 - (void)startProp {
317   if (self->propSet == nil)
318     self->propSet = [[NSMutableDictionary alloc] initWithCapacity:64];
319   
320   self->in.Prop = 1;
321   if (debugPropValue)
322     [self logWithFormat:@"start <prop> tag ..."];
323 }
324 - (void)endProp {
325   self->in.Prop = 0;
326 }
327
328 - (void)startTarget {
329   self->in.target = 1;
330   [self->targets removeAllObjects];
331   if (self->targets == nil) 
332     self->targets = [[NSMutableArray alloc] initWithCapacity:16];
333 }
334
335 - (void)startPropFindSpec:(NSString *)_localName {
336   unsigned len;
337   unichar  c;
338   NSString *fqn;
339   
340   if ((len = [_localName length]) == 0)
341     return;
342   c = [_localName characterAtIndex:0];
343     
344   if (c == 'a' && len == 7) {
345     if ([_localName isEqualToString:@"allprop"]) {
346       self->findAllProps = 1;
347       return;
348     }
349   }
350   if (c == 'p' && len == 8) {
351     if ([_localName isEqualToString:@"propname"]) {
352       self->findPropNames = 1;
353       return;
354     }
355   }
356     
357   fqn = [[NSString alloc] initWithFormat:@"{%@}%@", XMLNS_WEBDAV, _localName];
358   [self->propNames addObject:fqn];
359   [fqn release];
360 }
361
362 - (void)startResponseElement {
363   self->in.Response = 1;
364   
365   if (heavyLog) [self logWithFormat:@"clearing property-set"];
366   [self->propSet removeAllObjects];
367 }
368 - (void)endResponseElement {
369   self->in.Response = 0;
370   
371   if ([self->delegate respondsToSelector:
372              @selector(davHandler:receivedProperties:forURI:)]) {
373     [self->delegate
374          davHandler:self
375          receivedProperties:self->propSet
376          forURI:self->lastHref];
377   }
378   else if (self->response)
379     [self->responses addObject:self->response];
380 }
381
382 - (void)endBasicSearch {
383   /* only works with a single 'from', 'where' and 'select' */
384   NSMutableDictionary *hints;
385   
386   self->in.basicsearch = 0;
387   
388   if (self->lastScopeHref)
389     [self->fspec setEntityName:self->lastScopeHref];
390   
391   // qualifier
392   
393   hints = [[NSMutableDictionary alloc] initWithCapacity:8];
394   
395   if (self->lastScopeDepth) {
396     if ([self->lastScopeDepth isEqualToString:@"infinity"])
397       [hints setObject:@"deep" forKey:@"scope"];
398     else if ([self->lastScopeDepth isEqualToString:@"1"])
399       [hints setObject:@"flat+self" forKey:@"scope"];
400     else if ([self->lastScopeDepth isEqualToString:@"1,noroot"])
401       [hints setObject:@"flat" forKey:@"scope"];
402     else if ([self->lastScopeDepth isEqualToString:@"0"])
403       [hints setObject:@"self" forKey:@"scope"];
404     else {
405       [self logWithFormat:@"unknown search depth '%@'", self->lastScopeDepth];
406       [hints setObject:self->lastScopeDepth forKey:@"scope"];
407     }
408   }
409   
410   if (self->findPropNames)
411     [hints setObject:[NSNumber numberWithBool:YES] forKey:@"namesOnly"];
412   if (self->propNames)
413     [hints setObject:self->propNames forKey:@"attributes"];
414   /* Note: "allprops" is "no attributes set" in fspec */
415   
416   [self->fspec setHints:hints];
417   [hints release];
418   
419   // [self logWithFormat:@"parsed spec: %@", fspec];
420 }
421
422 - (void)addParsedQualifier:(EOQualifier *)_qualifier {
423   if (self->qualifiers)
424     [self->qualifiers addObject:_qualifier];
425   else {
426     //[self logWithFormat:@"got root qualifier: %@", _qualifier];
427     [self->fspec setQualifier:_qualifier];
428   }
429 }
430
431 - (void)endComparisonQualifier:(SEL)_op {
432   /* 
433      collect:
434      <D:eq>
435        <D:prop><D:sn/></D:prop>
436        <D:literal>Mueller</D:literal>
437      </D:eq>
438   */
439   EOKeyValueQualifier *kv;
440   
441   kv = [[EOKeyValueQualifier alloc] initWithKey:self->lastWherePropName
442                                     operatorSelector:_op
443                                     value:self->lastLiteral];
444   [self addParsedQualifier:kv];
445   [kv release];
446   
447   [self->lastWherePropName release]; self->lastWherePropName = nil;
448   [self->lastLiteral       release]; self->lastLiteral = nil;
449 }
450
451 - (void)beginCompoundQualifier {
452   if (self->compoundQualStack == nil)
453     self->compoundQualStack = [[NSMutableArray alloc] initWithCapacity:16];
454   
455   self->qualifiers = [[NSMutableArray alloc] initWithCapacity:4];
456   [self->compoundQualStack addObject:self->qualifiers];
457 }
458 - (void)endCompoundQualifier:(NSString *)_localName {
459   EOQualifier *q;
460   unsigned stackSize;
461   
462   if ([_localName isEqualToString:@"not"]) {
463     unsigned cnt;
464     
465     if ((cnt = [self->qualifiers count]) == 0)
466       q = nil;
467     else {
468       q = [self->qualifiers objectAtIndex:0];
469       if (cnt != 1) {
470         [self logWithFormat:@"WARNING: to many subqualifiers in not !: %@",
471                 self->qualifiers];
472       }
473     }
474     q = [[EONotQualifier alloc] initWithQualifier:q];
475   }
476   else {
477     Class qc = Nil;
478     
479     if ([_localName isEqualToString:@"and"])
480       qc = [EOAndQualifier class];
481     else if ([_localName isEqualToString:@"or"])
482       qc = [EOOrQualifier class];
483     else {
484       [self logWithFormat:@"unknown compound qualifier: '%@'", _localName];
485       qc = Nil;
486     }
487     
488     q = [[qc alloc] initWithQualifierArray:self->qualifiers];
489   }
490   [self->qualifiers release]; self->qualifiers = nil;
491   
492   if ((stackSize = [self->compoundQualStack count]) == 0) {
493     [self logWithFormat:@"ERROR: the qualifier stack is mixed up !"];
494   }
495   else if (stackSize > 1) {
496     /* this one was not the root qualifier */
497     [self->compoundQualStack removeObjectAtIndex:(stackSize - 1)];
498     self->qualifiers = [self->compoundQualStack objectAtIndex:(stackSize -2 )];
499   }
500   else
501     /* this one was the root qualifier */
502     [self->compoundQualStack removeObjectAtIndex:0];
503   
504   if (q) [self addParsedQualifier:q];
505   [q release];
506 }
507
508 - (void)startPropValueElement:(NSString *)_localName namespace:(NSString *)_ns {
509   self->propValueNesting++;
510   if (debugPropValue) {
511     [self debugWithFormat:@"start[%i]: {%@}%@", self->propValueNesting,
512             _ns, _localName];
513   }
514       
515   if (self->propValueNesting == 1) {
516     /* starting value */
517         
518     if (self->cdata) {
519       /* this can happen with nested tags ! */
520       [self logWithFormat:@"some cdata collection already in progress ?"];
521     }
522     else
523       self->cdata = [[NSMutableString alloc] initWithCapacity:256];
524   }
525   else {
526     /* add tag to value for later parsing */
527     [self->cdata appendString:@"<"];
528     [self->cdata appendString:@"V:"];
529     [self->cdata appendString:_localName];
530     [self->cdata appendString:@" xmlns:V=\""];
531     [self->cdata appendString:_ns];
532     [self->cdata appendString:@"\""];
533     [self->cdata appendString:@">"];
534   }
535 }
536 - (void)endPropValueElement:(NSString *)_localName namespace:(NSString *)_ns {
537   NSString *t;
538   NSString *fqn;
539   
540   if (debugPropValue) {
541     [self debugWithFormat:@"end[%i]: {%@}%@", self->propValueNesting,
542             _ns, _localName];
543   }
544   
545   if (self->propValueNesting == 1) {
546     fqn = [NSString stringWithFormat:@"{%@}%@", _ns, _localName];
547       
548     t = [self->cdata copy];
549     [self->cdata release]; self->cdata = nil;
550         
551     if (t) 
552       [self->propSet setObject:t forKey:fqn];
553     else {
554       [self->propSet setObject:[NSNull null] forKey:fqn];
555       [self logWithFormat:
556               @"ERROR: 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 */