]> err.no Git - sope/blob - sope-appserver/NGObjWeb/WebDAV/SoObject+SoDAVQuery.m
improved WebDAV property handling
[sope] / sope-appserver / NGObjWeb / WebDAV / SoObject+SoDAVQuery.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 "SoObject+SoDAV.h"
23 #include "SoObject.h"
24 #include "SoObjectDataSource.h"
25 #include "EOFetchSpecification+SoDAV.h"
26 #include <NGObjWeb/WOApplication.h>
27 #include <NGObjWeb/WOContext.h>
28 #include <NGObjWeb/WEClientCapabilities.h>
29 #import <EOControl/EOQualifier.h>
30 #import <EOControl/EOSortOrdering.h>
31 #include "common.h"
32
33 @implementation NSObject(SoObjectDAVQueries)
34
35 static int debugBulk = NO; // TODO: set to -1 and use defaults
36
37 - (NSEnumerator *)davChildKeysInContext:(id)_ctx {
38   /*
39     This returns the names of the children of a collection.
40     Could return toOneRelationshipKeys+toManyRelationshipKeys ? 
41   */
42   NSClassDescription *cd;
43   NSArray *t1, *tn;
44   
45   /* 
46      Note: this is done explicitly because the WebDAV class description
47            can be different to the 'EOF' class description.
48   */
49   if ((cd = [self soClassDescription]) != nil) {
50     t1 = [cd toOneRelationshipKeys];
51     tn = [cd toManyRelationshipKeys];
52   }
53   else {
54     t1 = [self toOneRelationshipKeys];
55     tn = [self toManyRelationshipKeys];
56   }
57   
58   if ([tn count] == 0)
59     return [t1 objectEnumerator];
60   if ([t1 count] == 0)
61     return [tn objectEnumerator];
62   
63   return [[t1 arrayByAddingObjectsFromArray:tn] objectEnumerator];
64 }
65
66 - (EODataSource *)contentDataSourceInContext:(id)_ctx {
67   return [[[SoObjectDataSource alloc] initWithObject:self
68                                       inContext:_ctx] autorelease];
69 }
70
71 - (EODataSource *)davFlatDataSourceInContext:(id)_ctx {
72   [self logWithFormat:
73           @"%s: this method is deprecated,use -contentDataSourceInContext: !"];
74   return [self contentDataSourceInContext:_ctx];
75 }
76
77 - (NSArray *)davQueryOnSelf:(EOFetchSpecification *)_fs inContext:(id)_ctx {
78   id<EOQualifierEvaluation> q;
79   NSDictionary *values;
80   NSArray *keys;
81   
82   if ((q = (void *)[_fs qualifier])) {
83     if (![q evaluateWithObject:self]) {
84       [self debugWithFormat:@"  self does not match qualifier."];
85       return [NSArray array];
86     }
87   }
88   
89   if ((keys = [_fs selectedWebDAVPropertyNames]) == nil) {
90     /*
91       Note: this should not happen anymore, a default-set will be used by
92       the dispatcher.
93     */
94     keys = [[self soClassDescription] attributeKeys];
95     [self debugWithFormat:@"using keys from description: %@", keys];
96   }
97   
98   /* ensure that the URL is added */
99   keys = [keys arrayByAddingObject:@"davURL"];
100   
101   if ([_fs queryWebDAVPropertyNamesOnly]) {
102     /* how does the renderer know, that these are the keys ? */
103     [self debugWithFormat:@"deliver keys only: %@", keys];
104     return keys != nil ? [NSArray arrayWithObject:keys] : nil;
105   }
106   
107   // TODO: we should map out certain keys, like 'retain', 'release' etc!
108   if ((values = [self valuesForKeys:keys]) == nil)
109     return nil;
110   
111   return [NSArray arrayWithObject:values];
112 }
113
114 - (id)performWebDAVDeepQuery:(EOFetchSpecification *)_fs inContext:(id)_ctx {
115   /* this just does a flat search :-(, maybe we should return 403 ? */
116   WEClientCapabilities *cc;
117   EODataSource *ds;
118   NSArray      *result;
119   NSString *scope;
120   BOOL     includeSelf, doDeep;
121   unsigned count;
122   
123   /* setup scope info */
124   
125   scope = [_fs scopeOfWebDAVQuery];
126   includeSelf = [scope rangeOfString:@"-self"].length == 0 ? NO : YES;
127   doDeep      = YES;
128   
129   /* do some user-agent specific tolerance */
130   
131   cc = [[(id <WOPageGenerationContext>)_ctx request] clientCapabilities];
132   if (includeSelf) {
133     NSString *ua;
134     
135     ua = [cc userAgentType];
136     if ([ua isEqualToString:@"Evolution"] || [ua isEqualToString:@"WebFolder"])
137       includeSelf = NO;
138     else {
139       [self logWithFormat:@"return self on UA: %@", ua];
140       includeSelf = YES;
141     }
142   }
143   doDeep = NO;
144   
145   /* perform deep query */
146   
147   ds = [self contentDataSourceInContext:_ctx];
148   [ds setFetchSpecification:_fs];
149   
150   if ((result = [ds fetchObjects]) == nil)
151     /* nil is error */
152     return nil;
153   
154   if ((count = [result count]) == 0) {
155     if (includeSelf)
156       return [self davQueryOnSelf:_fs inContext:_ctx];
157   }
158   else {
159     NSMutableArray *ma;
160     
161     ma = [NSMutableArray arrayWithCapacity:(count + 2)];
162     
163     if (includeSelf)
164       [ma addObjectsFromArray:[self davQueryOnSelf:_fs inContext:_ctx]];
165     
166     /* add flat results */
167     [ma addObjectsFromArray:result];
168     
169     if (doDeep) {
170       /* 
171          Should walk over each result and reperform the query with deep-self.
172          If the results came from SoObjectDataSource this is possible because
173          the "full" object is none in the SoObjectResultEntry.
174       */
175       [self warnWithFormat:@"attempted deep-search, not supported yet."];
176     }    
177     result = ma;
178   }
179   return result;
180 }
181
182 - (id)performWebDAVBulkQuery:(EOFetchSpecification *)_fs inContext:(id)_ctx {
183   NSAutoreleasePool    *pool;
184   EOFetchSpecification *subSpec;
185   NSMutableArray *results;
186   NSEnumerator *keys;
187   NSString     *key;
188
189   pool = [[NSAutoreleasePool alloc] init];
190   //[self debugWithFormat:@"perform bulk query ..."];
191   
192   /* create a sub-fetch-spec */
193   {
194     NSMutableDictionary *hints;
195     
196     hints = [[_fs hints] mutableCopy];
197     [hints removeObjectForKey:@"bulkTargetKeys"];
198     
199     subSpec = [[_fs copy] autorelease];
200     [subSpec setHints:hints];
201     [hints release];
202   }
203   
204   results = [NSMutableArray arrayWithCapacity:256];
205   keys = [[_fs davBulkTargetKeys] objectEnumerator];
206   while ((key = [keys nextObject])) {
207     id child;
208     id childResults;
209     
210     if (debugBulk)
211       [self debugWithFormat:@"  check bulk key: '%@'", key];
212     
213     /* lookup target */
214     
215     if ([key rangeOfString:@"/"].length == 0) {
216       /* simple key, just use -lookupName */
217       child = [self lookupName:key inContext:_ctx acquire:NO];
218       if (child == nil) {
219         [self errorWithFormat:@"did not find the BPROPFIND target '%@'", key];
220         continue;
221       }
222     }
223     else {
224       /* complex key, try to traverse */
225       // TODO: pass auth parameters to traversal context !!
226       child = [self traversePath:key acquire:NO];
227       if (child == nil) {
228         [self errorWithFormat:
229                 @"did not find the BPROPFIND target '%@' by traversing.",
230                 key];
231         continue;
232       }
233       if ([child isKindOfClass:[NSException class]]) {
234         [self logWithFormat:@"traversing bulk path '%@', failed: %@", 
235                 key, child];
236         continue;
237       }
238       [self logWithFormat:@"traversed bulk path '%@', got: %@", key, child];
239     }
240     
241     /* set entity name */
242     [subSpec setEntityName:
243                [[_fs entityName] stringByAppendingPathComponent:key]];
244     
245     /* perform subquery */
246     childResults = [child performWebDAVQuery:subSpec inContext:_ctx];
247     if (childResults) {
248       if ([childResults isKindOfClass:[NSArray class]])
249         [results addObjectsFromArray:childResults];
250       else
251         [results addObject:childResults];
252     }
253   }
254   
255   results = [results retain];
256   [pool release];
257   return [results autorelease];
258 }
259
260 - (id)performWebDAVQuery:(EOFetchSpecification *)_fs inContext:(id)_ctx {
261   NSString *scope;
262   NSArray  *bulkQueryKeys;
263   
264   if (_fs == nil) return nil;
265
266   if ((bulkQueryKeys = [_fs davBulkTargetKeys]))
267     return [self performWebDAVBulkQuery:_fs inContext:_ctx];
268   
269   scope = [_fs scopeOfWebDAVQuery];
270   if ([scope hasPrefix:@"flat"]) {
271     EODataSource *ds;
272     NSArray *result;
273     
274     ds = [self contentDataSourceInContext:_ctx];
275     [ds setFetchSpecification:_fs];
276     if ((result = [ds fetchObjects]) == nil)
277       /* error */
278       return nil;
279     
280     if ([scope rangeOfString:@"+self"].length > 0) {
281       /* should include self */
282       unsigned len;
283       
284       // TODO: don't add self if we work on ZideLook or WebFolders !
285       
286       if ((len = [result count]) == 0)
287         result = [self davQueryOnSelf:_fs inContext:_ctx];
288       else {
289         NSMutableArray *ma;
290         
291         ma = [NSMutableArray arrayWithCapacity:(len + 2)];
292         [ma addObjectsFromArray:[self davQueryOnSelf:_fs inContext:_ctx]];
293         [ma addObjectsFromArray:result];
294         result = ma;
295       }
296     }
297     return result;
298   }
299
300   if ([scope hasPrefix:@"self"])
301     return [self davQueryOnSelf:_fs inContext:_ctx];
302   
303   if ([scope hasPrefix:@"deep"])
304     return [self performWebDAVDeepQuery:_fs inContext:_ctx];
305   
306   [self errorWithFormat:@"called with invalid scope '%@'", scope];
307   return nil;
308 }
309
310 @end /* NSObject(SoObjectDAVQueries) */