2 Copyright (C) 2002-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
22 #include "SaxDAVHandler.h"
23 #include "EOFetchSpecification+SoDAV.h"
24 #include <SaxObjC/XMLNamespaces.h>
25 #include <EOControl/EOQualifier.h>
29 TODO: support parsing of DASL
30 basics are done, open are:
33 Breaks on SQL searches without Brief: T, which contain a 404 propstat:
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>
39 a set tag can be either in a "propertyupdate" or in a "response"
41 <set><prop><a>1</a></prop><prop><b>2</b></prop>
42 <response><prop><a>1</a><b>2</b></prop></response>
45 @implementation SaxDAVHandler
47 static BOOL debugPropValue = NO;
48 static BOOL heavyLog = NO;
51 static BOOL didInit = NO;
53 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
55 debugPropValue = [ud boolForKey:@"DAVParserDebugProp"];
56 heavyLog = [ud boolForKey:@"DAVParserHeavyLog"];
62 [self->locator release];
63 [self->propNames release];
64 [self->responses release];
65 [self->cdata release];
72 /* only reset non-result items */
73 if (heavyLog) [self logWithFormat:@"reset parser state"];
74 self->propValueNesting = 0;
75 self->in.PropFind = 0;
77 self->in.Response = 0;
78 self->in.MultiStatus = 0;
81 self->in.PropStat = 0;
82 self->in.PropertyUpdate = 0;
85 self->in.SearchRequest = 0;
86 self->in.basicsearch = 0;
100 self->in.ascending = 0;
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;
112 /* reset everything, including result items */
113 if (heavyLog) [self logWithFormat:@"reset"];
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;
129 - (void)setDelegate:(id)_delegate {
130 self->delegate = _delegate;
133 return self->delegate;
138 - (BOOL)propFindAllProperties {
139 return self->findAllProps;
141 - (BOOL)propFindPropertyNames {
142 return self->findPropNames;
144 - (NSArray *)propFindQueriedNames {
145 if ([self->propNames count] == 0) return nil;
146 return [[self->propNames copy] autorelease];
149 - (NSArray *)bpropFindTargets {
150 if ([self->targets count] == 0) return nil;
151 return [[self->targets copy] autorelease];
154 /* proppatch results */
156 - (NSArray *)propPatchPropertyNamesToRemove {
157 if ([self->propNames count] == 0) return nil;
158 return [[self->propNames copy] autorelease];
160 - (NSDictionary *)propPatchValues {
161 if ([self->propSet count] == 0) return nil;
162 return [[self->propSet copy] autorelease];
165 /* search query results */
167 - (EOFetchSpecification *)searchFetchSpecification {
168 EOFetchSpecification *fs;
170 if (heavyLog) [self logWithFormat:@"build search fetchspec"];
173 if (heavyLog) [self logWithFormat:@" use parsed fetchspec"];
177 if ([self->searchSQL length] == 0) {
178 if (heavyLog) [self logWithFormat:@" no SQL to parse"];
182 fs = [EOFetchSpecification parseWebDAVSQLString:self->searchSQL];
184 [self logWithFormat:@"could not parse SQL: '%@'", self->searchSQL];
187 if (heavyLog) [self logWithFormat:@" parsed: %@", fs];
191 /* positioning info */
193 - (void)setDocumentLocator:(id<NSObject,SaxLocator>)_locator {
194 ASSIGN(self->locator, _locator);
199 - (void)startDocument {
200 if (heavyLog) [self logWithFormat:@"start document"];
203 - (void)endDocument {
204 if (heavyLog) [self logWithFormat:@"end document"];
210 - (void)startSQLElement {
214 [self logWithFormat:@"some cdata collection already in progress ?"];
217 self->cdata = [[NSMutableString alloc] initWithCapacity:256];
219 - (void)endSQLElement {
220 /* TODO: could immediatly parse SQL into fetch-spec */
222 [self->searchSQL release]; self->searchSQL = nil;
223 self->searchSQL = [self->cdata copy];
224 [self->cdata release]; self->cdata = nil;
227 - (void)startLiteralElement {
228 self->in.literal = 1;
230 [self logWithFormat:@"some cdata collection already in progress ?"];
232 self->cdata = [[NSMutableString alloc] initWithCapacity:256];
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;
241 - (void)startHrefElement {
243 if (self->in.scope || self->in.target || self->in.Response) {
245 [self logWithFormat:@"some cdata collection already in progress ?"];
247 self->cdata = [[NSMutableString alloc] initWithCapacity:256];
250 - (void)endHrefElement {
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;
257 else if (self->in.target && self->cdata != nil) {
258 [self->targets addObject:self->cdata];
259 [self->cdata release]; self->cdata = nil;
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;
268 - (void)startDepthElement {
270 if (self->in.scope) {
272 [self logWithFormat:@"some cdata collection already in progress ?"];
274 self->cdata = [[NSMutableString alloc] initWithCapacity:256];
277 - (void)endDepthElement {
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;
286 - (void)startBasicSearch {
287 self->in.basicsearch = 1;
289 [self logWithFormat:@"basicsearch collection already in progress ?"];
291 self->fspec = [[EOFetchSpecification alloc] init];
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];
302 - (void)startPropSet {
304 if (self->propSet == nil)
305 self->propSet = [[NSMutableDictionary alloc] initWithCapacity:64];
307 [self logWithFormat:@"start <set> tag ..."];
311 if (debugPropValue) {
312 [self logWithFormat:@"end </set> tag (%i props in set) ...",
313 [self->propSet count]];
318 if (self->propSet == nil)
319 self->propSet = [[NSMutableDictionary alloc] initWithCapacity:64];
323 [self logWithFormat:@"start <prop> tag ..."];
329 - (void)startTarget {
331 [self->targets removeAllObjects];
332 if (self->targets == nil)
333 self->targets = [[NSMutableArray alloc] initWithCapacity:16];
336 - (void)startPropFindSpec:(NSString *)_localName {
341 if ((len = [_localName length]) == 0)
343 c = [_localName characterAtIndex:0];
345 if (c == 'a' && len == 7) {
346 if ([_localName isEqualToString:@"allprop"]) {
347 self->findAllProps = 1;
351 if (c == 'p' && len == 8) {
352 if ([_localName isEqualToString:@"propname"]) {
353 self->findPropNames = 1;
358 fqn = [[NSString alloc] initWithFormat:@"{%@}%@", XMLNS_WEBDAV, _localName];
359 [self->propNames addObject:fqn];
363 - (void)startResponseElement {
364 self->in.Response = 1;
366 if (heavyLog) [self logWithFormat:@"clearing property-set"];
367 [self->propSet removeAllObjects];
369 - (void)endResponseElement {
370 self->in.Response = 0;
372 if ([self->delegate respondsToSelector:
373 @selector(davHandler:receivedProperties:forURI:)]) {
376 receivedProperties:self->propSet
377 forURI:self->lastHref];
379 else if (self->response)
380 [self->responses addObject:self->response];
383 - (void)endBasicSearch {
384 /* only works with a single 'from', 'where' and 'select' */
385 NSMutableDictionary *hints;
387 self->in.basicsearch = 0;
389 if (self->lastScopeHref)
390 [self->fspec setEntityName:self->lastScopeHref];
394 hints = [[NSMutableDictionary alloc] initWithCapacity:8];
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"];
406 [self logWithFormat:@"unknown search depth '%@'", self->lastScopeDepth];
407 [hints setObject:self->lastScopeDepth forKey:@"scope"];
411 if (self->findPropNames)
412 [hints setObject:[NSNumber numberWithBool:YES] forKey:@"namesOnly"];
414 [hints setObject:self->propNames forKey:@"attributes"];
415 /* Note: "allprops" is "no attributes set" in fspec */
417 [self->fspec setHints:hints];
420 // [self logWithFormat:@"parsed spec: %@", fspec];
423 - (void)addParsedQualifier:(EOQualifier *)_qualifier {
424 if (self->qualifiers)
425 [self->qualifiers addObject:_qualifier];
427 //[self logWithFormat:@"got root qualifier: %@", _qualifier];
428 [self->fspec setQualifier:_qualifier];
432 - (void)endComparisonQualifier:(SEL)_op {
436 <D:prop><D:sn/></D:prop>
437 <D:literal>Mueller</D:literal>
440 EOKeyValueQualifier *kv;
442 kv = [[EOKeyValueQualifier alloc] initWithKey:self->lastWherePropName
444 value:self->lastLiteral];
445 [self addParsedQualifier:kv];
448 [self->lastWherePropName release]; self->lastWherePropName = nil;
449 [self->lastLiteral release]; self->lastLiteral = nil;
452 - (void)beginCompoundQualifier {
453 if (self->compoundQualStack == nil)
454 self->compoundQualStack = [[NSMutableArray alloc] initWithCapacity:16];
456 self->qualifiers = [[NSMutableArray alloc] initWithCapacity:4];
457 [self->compoundQualStack addObject:self->qualifiers];
459 - (void)endCompoundQualifier:(NSString *)_localName {
463 if ([_localName isEqualToString:@"not"]) {
466 if ((cnt = [self->qualifiers count]) == 0)
469 q = [self->qualifiers objectAtIndex:0];
471 [self warnWithFormat:@"too many subqualifiers in not !: %@",
475 q = [[EONotQualifier alloc] initWithQualifier:q];
480 if ([_localName isEqualToString:@"and"])
481 qc = [EOAndQualifier class];
482 else if ([_localName isEqualToString:@"or"])
483 qc = [EOOrQualifier class];
485 [self logWithFormat:@"unknown compound qualifier: '%@'", _localName];
489 q = [[qc alloc] initWithQualifierArray:self->qualifiers];
491 [self->qualifiers release]; self->qualifiers = nil;
493 if ((stackSize = [self->compoundQualStack count]) == 0) {
494 [self errorWithFormat:@"the qualifier stack is mixed up !"];
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 )];
502 /* this one was the root qualifier */
503 [self->compoundQualStack removeObjectAtIndex:0];
505 if (q) [self addParsedQualifier:q];
509 - (void)startPropValueElement:(NSString *)_localName namespace:(NSString *)_ns {
510 self->propValueNesting++;
511 if (debugPropValue) {
512 [self debugWithFormat:@"start[%i]: {%@}%@", self->propValueNesting,
516 if (self->propValueNesting == 1) {
520 /* this can happen with nested tags ! */
521 [self logWithFormat:@"some cdata collection already in progress ?"];
524 self->cdata = [[NSMutableString alloc] initWithCapacity:256];
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:@">"];
537 - (void)endPropValueElement:(NSString *)_localName namespace:(NSString *)_ns {
541 if (debugPropValue) {
542 [self debugWithFormat:@"end[%i]: {%@}%@", self->propValueNesting,
546 if (self->propValueNesting == 1) {
547 fqn = [NSString stringWithFormat:@"{%@}%@", _ns, _localName];
549 t = [self->cdata copy];
550 [self->cdata release]; self->cdata = nil;
553 [self->propSet setObject:t forKey:fqn];
555 [self->propSet setObject:[NSNull null] forKey:fqn];
556 [self errorWithFormat:@"lost the parsing cdata (broken nesting) ?!"];
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:@">"];
568 self->propValueNesting--;
571 /* dav element dispatcher */
573 - (void)startDavElement:(NSString *)_localName {
577 if ((len = [_localName length]) == 0)
580 if ((self->in.PropFind || self->in.select) && self->in.Prop) {
581 [self startPropFindSpec:_localName];
584 if (self->in.where && self->in.Prop) {
585 /* a property in a where */
587 fqn = [NSString stringWithFormat:@"{%@}%@", XMLNS_WEBDAV, _localName];
588 ASSIGNCOPY(self->lastWherePropName, fqn);
589 /* TODO: should return ? */
591 if (self->in.PropertyUpdate && self->in.Set && self->in.Prop) {
592 [self startPropValueElement:_localName namespace:XMLNS_WEBDAV];
595 if (self->in.Response && self->in.PropStat && self->in.Prop) {
596 [self startPropValueElement:_localName namespace:XMLNS_WEBDAV];
600 c = [_localName characterAtIndex:0];
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];
612 if ([_localName isEqualToString:@"basicsearch"])
613 [self startBasicSearch];
617 if ([_localName isEqualToString:@"depth"])
618 [self startDepthElement];
622 if ([_localName isEqualToString:@"eq"])
627 if ([_localName isEqualToString:@"from"])
632 if ([_localName isEqualToString:@"gt"])
634 else if ([_localName isEqualToString:@"gte"])
639 if ([_localName isEqualToString:@"href"])
640 [self startHrefElement];
644 if ([_localName isEqualToString:@"lt"])
646 else if ([_localName isEqualToString:@"lte"])
648 else if ([_localName isEqualToString:@"literal"])
649 [self startLiteralElement];
653 if ([_localName isEqualToString:@"multistatus"]) {
654 self->in.MultiStatus = 1;
655 if (self->responses == nil)
656 self->responses = [[NSMutableArray alloc] initWithCapacity:64];
661 if ([_localName isEqualToString:@"not"])
662 [self beginCompoundQualifier];
666 if ([_localName isEqualToString:@"order"])
668 else if ([_localName isEqualToString:@"orderby"])
669 self->in.orderby = 1;
670 else if ([_localName isEqualToString:@"or"])
671 [self beginCompoundQualifier];
675 if ([_localName isEqualToString:@"propfind"]) {
676 if (self->propNames == nil)
677 self->propNames = [[NSMutableArray alloc] initWithCapacity:64];
678 self->in.PropFind = 1;
680 else if ([_localName isEqualToString:@"prop"])
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;
692 if ([_localName isEqualToString:@"response"])
693 [self startResponseElement];
694 else if ([_localName isEqualToString:@"remove"])
699 if ([_localName isEqualToString:@"status"])
701 else if ([_localName isEqualToString:@"set"])
703 else if ([_localName isEqualToString:@"select"])
705 else if ([_localName isEqualToString:@"scope"])
707 else if ([_localName isEqualToString:@"searchrequest"])
708 self->in.SearchRequest = 1;
709 else if ([_localName isEqualToString:@"sql"])
710 [self startSQLElement];
714 if ([_localName isEqualToString:@"target"])
719 if ([_localName isEqualToString:@"where"])
727 - (void)endDavElement:(NSString *)_localName {
731 if ((len = [_localName length]) == 0)
733 c = [_localName characterAtIndex:0];
735 if (self->in.PropertyUpdate && self->in.Set && self->in.Prop) {
736 if (![_localName isEqualToString:@"prop"]) {
737 [self endPropValueElement:_localName namespace:XMLNS_WEBDAV];
741 if (self->in.Response && self->in.PropStat && self->in.Prop) {
742 if (![_localName isEqualToString:@"prop"]) {
743 [self endPropValueElement:_localName namespace:XMLNS_WEBDAV];
750 if ([_localName isEqualToString:@"and"])
751 [self endCompoundQualifier:@"and"];
754 if ([_localName isEqualToString:@"basicsearch"])
755 [self endBasicSearch];
759 if ([_localName isEqualToString:@"depth"])
760 [self endDepthElement];
764 if ([_localName isEqualToString:@"eq"]) {
765 [self endComparisonQualifier:EOQualifierOperatorEqual];
771 if ([_localName isEqualToString:@"from"])
776 if ([_localName isEqualToString:@"gt"]) {
777 [self endComparisonQualifier:EOQualifierOperatorGreaterThan];
780 else if ([_localName isEqualToString:@"gte"]) {
781 [self endComparisonQualifier:EOQualifierOperatorGreaterThanOrEqualTo];
787 if ([_localName isEqualToString:@"href"])
788 [self endHrefElement];
792 if ([_localName isEqualToString:@"lt"]) {
793 [self endComparisonQualifier:EOQualifierOperatorLessThan];
796 else if ([_localName isEqualToString:@"lte"]) {
797 [self endComparisonQualifier:EOQualifierOperatorLessThanOrEqualTo];
800 else if ([_localName isEqualToString:@"literal"])
801 [self endLiteralElement];
805 if ([_localName isEqualToString:@"multistatus"])
806 self->in.MultiStatus = 0;
810 if ([_localName isEqualToString:@"not"])
811 [self endCompoundQualifier:@"not"];
815 if ([_localName isEqualToString:@"order"])
817 else if ([_localName isEqualToString:@"orderby"])
818 self->in.orderby = 0;
819 else if ([_localName isEqualToString:@"or"])
820 [self endCompoundQualifier:@"or"];
824 if ([_localName isEqualToString:@"propfind"])
825 self->in.PropFind = 0;
826 else if ([_localName isEqualToString:@"prop"])
828 else if ([_localName isEqualToString:@"propstat"])
829 self->in.PropStat = 0;
830 else if ([_localName isEqualToString:@"propertyupdate"])
831 self->in.PropertyUpdate = 0;
835 if ([_localName isEqualToString:@"response"])
836 [self endResponseElement];
837 else if ([_localName isEqualToString:@"remove"])
842 if ([_localName isEqualToString:@"set"])
844 else if ([_localName isEqualToString:@"select"])
846 else if ([_localName isEqualToString:@"scope"])
848 else if ([_localName isEqualToString:@"searchrequest"])
849 self->in.SearchRequest = 0;
851 else if ([_localName isEqualToString:@"sql"])
852 [self endSQLElement];
853 else if ([_localName isEqualToString:@"status"])
858 if ([_localName isEqualToString:@"target"])
863 if ([_localName isEqualToString:@"where"])
869 /* element callbacks */
871 - (void)startElement:(NSString *)_localName
872 namespace:(NSString *)_ns
873 rawName:(NSString *)_rawName
874 attributes:(id<SaxAttributes>)_attributes
876 if (heavyLog) [self logWithFormat:@"START {%@}%@", _ns, _localName];
878 if ([_ns isEqualToString:XMLNS_WEBDAV]) {
879 [self startDavElement:_localName];
881 else if (self->in.PropFind || self->in.select) {
883 fqn = [NSString stringWithFormat:@"{%@}%@", _ns, _localName];
884 [self->propNames addObject:fqn];
886 else if (self->in.where && self->in.Prop) {
888 fqn = [NSString stringWithFormat:@"{%@}%@", _ns, _localName];
889 ASSIGNCOPY(self->lastWherePropName, fqn);
891 else if (self->in.PropertyUpdate) {
893 [self startPropValueElement:_localName namespace:_ns];
895 else if (self->in.Remove) {
898 fqn = [NSString stringWithFormat:@"{%@}%@", _ns, _localName];
899 [self->propNames addObject:fqn];
902 else if (self->in.Response && self->in.PropStat && self->in.Prop)
903 [self startPropValueElement:_localName namespace:_ns];
906 - (void)endElement:(NSString *)_localName
907 namespace:(NSString *)_ns
908 rawName:(NSString *)_rawName
910 if (heavyLog) [self logWithFormat:@"END {%@}%@", _ns, _localName];
912 if ([_ns isEqualToString:XMLNS_WEBDAV]) {
913 [self endDavElement:_localName];
915 else if (self->in.PropFind) {
916 /* no close tags in propfind ... */
918 else if (self->in.PropertyUpdate) {
920 [self endPropValueElement:_localName namespace:_ns];
921 else if (self->in.Remove) {
922 /* no close tags in remove section ... */
925 else if (self->in.Response && self->in.PropStat && self->in.Prop)
926 [self endPropValueElement:_localName namespace:_ns];
931 - (void)characters:(unichar *)_chars length:(int)_len {
932 if (heavyLog) [self logWithFormat:@"got %i chars", _len];
934 if (_len > 0 && (self->cdata != nil)) {
937 s = [[NSString alloc] initWithCharacters:_chars length:_len];
938 [self->cdata appendString:s];
943 @end /* SaxDAVHandler */