2 Copyright (C) 2000-2003 SKYRIX Software AG
4 This file is part of OGo
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
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.
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
23 #include "SaxDAVHandler.h"
24 #include "EOFetchSpecification+SoDAV.h"
25 #include <SaxObjC/XMLNamespaces.h>
26 #include <EOControl/EOQualifier.h>
30 TODO: support parsing of DASL
31 basics are done, open are:
34 Breaks on SQL searches without Brief: T, which contain a 404 propstat:
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>
40 a set tag can be either in a "propertyupdate" or in a "response"
42 <set><prop><a>1</a></prop><prop><b>2</b></prop>
43 <response><prop><a>1</a><b>2</b></prop></response>
46 @implementation SaxDAVHandler
48 static BOOL debugPropValue = NO;
49 static BOOL heavyLog = NO;
52 static BOOL didInit = NO;
54 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
56 debugPropValue = [ud boolForKey:@"DAVParserDebugProp"];
57 heavyLog = [ud boolForKey:@"DAVParserHeavyLog"];
63 [self->locator release];
64 [self->propNames release];
65 [self->responses release];
66 [self->cdata release];
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 if (heavyLog) [self logWithFormat:@"reset"];
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;
128 - (void)setDelegate:(id)_delegate {
129 self->delegate = _delegate;
132 return self->delegate;
137 - (BOOL)propFindAllProperties {
138 return self->findAllProps;
140 - (BOOL)propFindPropertyNames {
141 return self->findPropNames;
143 - (NSArray *)propFindQueriedNames {
144 if ([self->propNames count] == 0) return nil;
145 return [[self->propNames copy] autorelease];
148 - (NSArray *)bpropFindTargets {
149 if ([self->targets count] == 0) return nil;
150 return [[self->targets copy] autorelease];
153 /* proppatch results */
155 - (NSArray *)propPatchPropertyNamesToRemove {
156 if ([self->propNames count] == 0) return nil;
157 return [[self->propNames copy] autorelease];
159 - (NSDictionary *)propPatchValues {
160 if ([self->propSet count] == 0) return nil;
161 return [[self->propSet copy] autorelease];
164 /* search query results */
166 - (EOFetchSpecification *)searchFetchSpecification {
167 EOFetchSpecification *fs;
169 if (heavyLog) [self logWithFormat:@"build search fetchspec"];
172 if (heavyLog) [self logWithFormat:@" use parsed fetchspec"];
176 if ([self->searchSQL length] == 0) {
177 if (heavyLog) [self logWithFormat:@" no SQL to parse"];
181 fs = [EOFetchSpecification parseWebDAVSQLString:self->searchSQL];
183 [self logWithFormat:@"could not parse SQL: '%@'", self->searchSQL];
186 if (heavyLog) [self logWithFormat:@" parsed: %@", fs];
190 /* positioning info */
192 - (void)setDocumentLocator:(id<NSObject,SaxLocator>)_locator {
193 ASSIGN(self->locator, _locator);
198 - (void)startDocument {
199 if (heavyLog) [self logWithFormat:@"start document"];
202 - (void)endDocument {
203 if (heavyLog) [self logWithFormat:@"end document"];
209 - (void)startSQLElement {
213 [self logWithFormat:@"some cdata collection already in progress ?"];
216 self->cdata = [[NSMutableString alloc] initWithCapacity:256];
218 - (void)endSQLElement {
219 /* TODO: could immediatly parse SQL into fetch-spec */
221 [self->searchSQL release]; self->searchSQL = nil;
222 self->searchSQL = [self->cdata copy];
223 [self->cdata release]; self->cdata = nil;
226 - (void)startLiteralElement {
227 self->in.literal = 1;
229 [self logWithFormat:@"some cdata collection already in progress ?"];
231 self->cdata = [[NSMutableString alloc] initWithCapacity:256];
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;
240 - (void)startHrefElement {
242 if (self->in.scope || self->in.target || self->in.Response) {
244 [self logWithFormat:@"some cdata collection already in progress ?"];
246 self->cdata = [[NSMutableString alloc] initWithCapacity:256];
249 - (void)endHrefElement {
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;
256 else if (self->in.target && self->cdata != nil) {
257 [self->targets addObject:self->cdata];
258 [self->cdata release]; self->cdata = nil;
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;
267 - (void)startDepthElement {
269 if (self->in.scope) {
271 [self logWithFormat:@"some cdata collection already in progress ?"];
273 self->cdata = [[NSMutableString alloc] initWithCapacity:256];
276 - (void)endDepthElement {
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;
285 - (void)startBasicSearch {
286 self->in.basicsearch = 1;
288 [self logWithFormat:@"basicsearch collection already in progress ?"];
290 self->fspec = [[EOFetchSpecification alloc] init];
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];
301 - (void)startPropSet {
303 if (self->propSet == nil)
304 self->propSet = [[NSMutableDictionary alloc] initWithCapacity:64];
306 [self logWithFormat:@"start <set> tag ..."];
310 if (debugPropValue) {
311 [self logWithFormat:@"end </set> tag (%i props in set) ...",
312 [self->propSet count]];
317 if (self->propSet == nil)
318 self->propSet = [[NSMutableDictionary alloc] initWithCapacity:64];
322 [self logWithFormat:@"start <prop> tag ..."];
328 - (void)startTarget {
330 [self->targets removeAllObjects];
331 if (self->targets == nil)
332 self->targets = [[NSMutableArray alloc] initWithCapacity:16];
335 - (void)startPropFindSpec:(NSString *)_localName {
340 if ((len = [_localName length]) == 0)
342 c = [_localName characterAtIndex:0];
344 if (c == 'a' && len == 7) {
345 if ([_localName isEqualToString:@"allprop"]) {
346 self->findAllProps = 1;
350 if (c == 'p' && len == 8) {
351 if ([_localName isEqualToString:@"propname"]) {
352 self->findPropNames = 1;
357 fqn = [[NSString alloc] initWithFormat:@"{%@}%@", XMLNS_WEBDAV, _localName];
358 [self->propNames addObject:fqn];
362 - (void)startResponseElement {
363 self->in.Response = 1;
365 if (heavyLog) [self logWithFormat:@"clearing property-set"];
366 [self->propSet removeAllObjects];
368 - (void)endResponseElement {
369 self->in.Response = 0;
371 if ([self->delegate respondsToSelector:
372 @selector(davHandler:receivedProperties:forURI:)]) {
375 receivedProperties:self->propSet
376 forURI:self->lastHref];
378 else if (self->response)
379 [self->responses addObject:self->response];
382 - (void)endBasicSearch {
383 /* only works with a single 'from', 'where' and 'select' */
384 NSMutableDictionary *hints;
386 self->in.basicsearch = 0;
388 if (self->lastScopeHref)
389 [self->fspec setEntityName:self->lastScopeHref];
393 hints = [[NSMutableDictionary alloc] initWithCapacity:8];
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"];
405 [self logWithFormat:@"unknown search depth '%@'", self->lastScopeDepth];
406 [hints setObject:self->lastScopeDepth forKey:@"scope"];
410 if (self->findPropNames)
411 [hints setObject:[NSNumber numberWithBool:YES] forKey:@"namesOnly"];
413 [hints setObject:self->propNames forKey:@"attributes"];
414 /* Note: "allprops" is "no attributes set" in fspec */
416 [self->fspec setHints:hints];
419 // [self logWithFormat:@"parsed spec: %@", fspec];
422 - (void)addParsedQualifier:(EOQualifier *)_qualifier {
423 if (self->qualifiers)
424 [self->qualifiers addObject:_qualifier];
426 //[self logWithFormat:@"got root qualifier: %@", _qualifier];
427 [self->fspec setQualifier:_qualifier];
431 - (void)endComparisonQualifier:(SEL)_op {
435 <D:prop><D:sn/></D:prop>
436 <D:literal>Mueller</D:literal>
439 EOKeyValueQualifier *kv;
441 kv = [[EOKeyValueQualifier alloc] initWithKey:self->lastWherePropName
443 value:self->lastLiteral];
444 [self addParsedQualifier:kv];
447 [self->lastWherePropName release]; self->lastWherePropName = nil;
448 [self->lastLiteral release]; self->lastLiteral = nil;
451 - (void)beginCompoundQualifier {
452 if (self->compoundQualStack == nil)
453 self->compoundQualStack = [[NSMutableArray alloc] initWithCapacity:16];
455 self->qualifiers = [[NSMutableArray alloc] initWithCapacity:4];
456 [self->compoundQualStack addObject:self->qualifiers];
458 - (void)endCompoundQualifier:(NSString *)_localName {
462 if ([_localName isEqualToString:@"not"]) {
465 if ((cnt = [self->qualifiers count]) == 0)
468 q = [self->qualifiers objectAtIndex:0];
470 [self logWithFormat:@"WARNING: to many subqualifiers in not !: %@",
474 q = [[EONotQualifier alloc] initWithQualifier:q];
479 if ([_localName isEqualToString:@"and"])
480 qc = [EOAndQualifier class];
481 else if ([_localName isEqualToString:@"or"])
482 qc = [EOOrQualifier class];
484 [self logWithFormat:@"unknown compound qualifier: '%@'", _localName];
488 q = [[qc alloc] initWithQualifierArray:self->qualifiers];
490 [self->qualifiers release]; self->qualifiers = nil;
492 if ((stackSize = [self->compoundQualStack count]) == 0) {
493 [self logWithFormat:@"ERROR: the qualifier stack is mixed up !"];
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 )];
501 /* this one was the root qualifier */
502 [self->compoundQualStack removeObjectAtIndex:0];
504 if (q) [self addParsedQualifier:q];
508 - (void)startPropValueElement:(NSString *)_localName namespace:(NSString *)_ns {
509 self->propValueNesting++;
510 if (debugPropValue) {
511 [self debugWithFormat:@"start[%i]: {%@}%@", self->propValueNesting,
515 if (self->propValueNesting == 1) {
519 /* this can happen with nested tags ! */
520 [self logWithFormat:@"some cdata collection already in progress ?"];
523 self->cdata = [[NSMutableString alloc] initWithCapacity:256];
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:@">"];
536 - (void)endPropValueElement:(NSString *)_localName namespace:(NSString *)_ns {
540 if (debugPropValue) {
541 [self debugWithFormat:@"end[%i]: {%@}%@", self->propValueNesting,
545 if (self->propValueNesting == 1) {
546 fqn = [NSString stringWithFormat:@"{%@}%@", _ns, _localName];
548 t = [self->cdata copy];
549 [self->cdata release]; self->cdata = nil;
552 [self->propSet setObject:t forKey:fqn];
554 [self->propSet setObject:[NSNull null] forKey:fqn];
556 @"ERROR: 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 */