]> err.no Git - sope/blob - sope-appserver/NGObjWeb/WebDAV/SoObject+SoDAVQuery.m
minor url optimization
[sope] / sope-appserver / NGObjWeb / WebDAV / SoObject+SoDAVQuery.m
1 /*
2   Copyright (C) 2002-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
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
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 #include <EOControl/EOQualifier.h>
30 #include <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 logWithFormat:
176               @"WARNING: attempted deep-search, not supported yet."];
177     }    
178     result = ma;
179   }
180   return result;
181 }
182
183 - (id)performWebDAVBulkQuery:(EOFetchSpecification *)_fs inContext:(id)_ctx {
184   NSAutoreleasePool    *pool;
185   EOFetchSpecification *subSpec;
186   NSMutableArray *results;
187   NSEnumerator *keys;
188   NSString     *key;
189
190   pool = [[NSAutoreleasePool alloc] init];
191   //[self debugWithFormat:@"perform bulk query ..."];
192   
193   /* create a sub-fetch-spec */
194   {
195     NSMutableDictionary *hints;
196     
197     hints = [[_fs hints] mutableCopy];
198     [hints removeObjectForKey:@"bulkTargetKeys"];
199     
200     subSpec = [[_fs copy] autorelease];
201     [subSpec setHints:hints];
202     [hints release];
203   }
204   
205   results = [NSMutableArray arrayWithCapacity:256];
206   keys = [[_fs davBulkTargetKeys] objectEnumerator];
207   while ((key = [keys nextObject])) {
208     id child;
209     id childResults;
210     
211     if (debugBulk)
212       [self debugWithFormat:@"  check bulk key: '%@'", key];
213     
214     /* lookup target */
215     
216     if ([key rangeOfString:@"/"].length == 0) {
217       /* simple key, just use -lookupName */
218       child = [self lookupName:key inContext:_ctx acquire:NO];
219       if (child == nil) {
220         [self logWithFormat:@"ERROR: did not find the BPROPFIND target '%@'",
221                 key];
222         continue;
223       }
224     }
225     else {
226       /* complex key, try to traverse */
227       // TODO: pass auth parameters to traversal context !!
228       child = [self traversePath:key acquire:NO];
229       if (child == nil) {
230         [self logWithFormat:
231                 @"ERROR: did not find the BPROPFIND target '%@' by traversing.",
232                 key];
233         continue;
234       }
235       if ([child isKindOfClass:[NSException class]]) {
236         [self logWithFormat:@"traversing bulk path '%@', failed: %@", 
237                 key, child];
238         continue;
239       }
240       [self logWithFormat:@"traversed bulk path '%@', got: %@", key, child];
241     }
242     
243     /* set entity name */
244     [subSpec setEntityName:
245                [[_fs entityName] stringByAppendingPathComponent:key]];
246     
247     /* perform subquery */
248     childResults = [child performWebDAVQuery:subSpec inContext:_ctx];
249     if (childResults) {
250       if ([childResults isKindOfClass:[NSArray class]])
251         [results addObjectsFromArray:childResults];
252       else
253         [results addObject:childResults];
254     }
255   }
256   
257   results = [results retain];
258   [pool release];
259   return [results autorelease];
260 }
261
262 - (id)performWebDAVQuery:(EOFetchSpecification *)_fs inContext:(id)_ctx {
263   NSString *scope;
264   NSArray  *bulkQueryKeys;
265   
266   if (_fs == nil) return nil;
267
268   if ((bulkQueryKeys = [_fs davBulkTargetKeys]))
269     return [self performWebDAVBulkQuery:_fs inContext:_ctx];
270   
271   scope = [_fs scopeOfWebDAVQuery];
272   if ([scope hasPrefix:@"flat"]) {
273     EODataSource *ds;
274     NSArray *result;
275     
276     ds = [self contentDataSourceInContext:_ctx];
277     [ds setFetchSpecification:_fs];
278     if ((result = [ds fetchObjects]) == nil)
279       /* error */
280       return nil;
281     
282     if ([scope rangeOfString:@"+self"].length > 0) {
283       /* should include self */
284       unsigned len;
285       
286       // TODO: don't add self if we work on ZideLook or WebFolders !
287       
288       if ((len = [result count]) == 0)
289         result = [self davQueryOnSelf:_fs inContext:_ctx];
290       else {
291         NSMutableArray *ma;
292         
293         ma = [NSMutableArray arrayWithCapacity:(len + 2)];
294         [ma addObjectsFromArray:[self davQueryOnSelf:_fs inContext:_ctx]];
295         [ma addObjectsFromArray:result];
296         result = ma;
297       }
298     }
299     return result;
300   }
301
302   if ([scope hasPrefix:@"self"])
303     return [self davQueryOnSelf:_fs inContext:_ctx];
304   
305   if ([scope hasPrefix:@"deep"])
306     return [self performWebDAVDeepQuery:_fs inContext:_ctx];
307   
308   [self logWithFormat:@"ERROR: called with invalid scope '%@'", scope];
309   return nil;
310 }
311
312 @end /* NSObject(SoObjectDAVQueries) */