]> err.no Git - scalable-opengroupware.org/blob - SoObjects/SOGo/SOGoFolder.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1051 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / SoObjects / SOGo / SOGoFolder.m
1 /*
2   Copyright (C) 2004-2005 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 #import <NGObjWeb/SoObject.h>
23 #import <GDLAccess/EOAdaptorChannel.h>
24 #import <GDLContentStore/GCSFolderManager.h>
25 #import <GDLContentStore/GCSFolder.h>
26 #import <GDLContentStore/GCSFolderType.h>
27
28 #import "SOGoPermissions.h"
29 #import "SOGoFolder.h"
30 #import "common.h"
31 #import <unistd.h>
32 #import <stdlib.h>
33
34 static NSString *defaultUser = @"<default>";
35
36 @implementation SOGoFolder
37
38 + (int) version
39 {
40   return [super version] + 0 /* v0 */;
41 }
42
43 + (void) initialize
44 {
45   NSAssert2([super version] == 0,
46             @"invalid superclass (%@) version %i !",
47             NSStringFromClass([self superclass]), [super version]);
48 }
49
50 + (NSString *) globallyUniqueObjectId
51 {
52   /*
53     4C08AE1A-A808-11D8-AC5A-000393BBAFF6
54     SOGo-Web-28273-18283-288182
55     printf( "%x", *(int *) &f);
56   */
57   static int pid = 0;
58   static int sequence = 0;
59   static float rndm = 0;
60   float f;
61
62   if (pid == 0)
63     { /* break if we fork ;-) */
64       pid = getpid();
65       rndm = random();
66     }
67   sequence++;
68   f = [[NSDate date] timeIntervalSince1970];
69   return [NSString stringWithFormat:@"%0X-%0X-%0X-%0X",
70                    pid, *(int *)&f, sequence++, random];
71 }
72
73 - (id) init
74 {
75   if ((self = [super init]))
76     {
77       ocsPath = nil;
78       ocsFolder = nil;
79       aclCache = [NSMutableDictionary new];
80     }
81
82   return self;
83 }
84
85 - (void) dealloc
86 {
87   [ocsFolder release];
88   [ocsPath release];
89   [aclCache release];
90   [super dealloc];
91 }
92
93 /* accessors */
94
95 - (BOOL) isFolderish
96 {
97   return YES;
98 }
99
100 - (void) setOCSPath: (NSString *) _path
101 {
102   if ([ocsPath isEqualToString:_path])
103     return;
104   
105   if (ocsPath)
106     [self warnWithFormat:@"GCS path is already set! '%@'", _path];
107   
108   ASSIGNCOPY(ocsPath, _path);
109 }
110
111 - (NSString *) ocsPath
112 {
113   return ocsPath;
114 }
115
116 - (GCSFolderManager *) folderManager
117 {
118   static GCSFolderManager *folderManager = nil;
119
120   if (!folderManager)
121     {
122       folderManager = [GCSFolderManager defaultFolderManager];
123       [folderManager setFolderNamePrefix: @"SOGo_"];
124     }
125
126   return folderManager;
127 }
128
129 - (GCSFolder *) ocsFolderForPath: (NSString *) _path
130 {
131   return [[self folderManager] folderAtPath:_path];
132 }
133
134 - (GCSFolder *) ocsFolder
135 {
136   GCSFolder *folder;
137
138   if (!ocsFolder)
139     ocsFolder = [[self ocsFolderForPath:[self ocsPath]] retain];
140
141   if ([ocsFolder isNotNull])
142     folder = ocsFolder;
143   else
144     folder = nil;
145
146   return folder;
147 }
148
149 - (NSString *) folderType
150 {
151   return @"";
152 }
153
154 - (BOOL) create
155 {
156   NSException *result;
157
158   result = [[self folderManager] createFolderOfType: [self folderType]
159                                  atPath: ocsPath];
160
161   return (result == nil);
162 }
163
164 - (NSException *) delete
165 {
166   return [[self folderManager] deleteFolderAtPath: ocsPath];
167 }
168
169 - (NSArray *)fetchContentObjectNames {
170   NSArray *fields, *records;
171   
172   fields = [NSArray arrayWithObject:@"c_name"];
173   records = [[self ocsFolder] fetchFields:fields matchingQualifier:nil];
174   if (![records isNotNull]) {
175     [self errorWithFormat:@"(%s): fetch failed!", __PRETTY_FUNCTION__];
176     return nil;
177   }
178   if ([records isKindOfClass:[NSException class]])
179     return records;
180   return [records valueForKey:@"c_name"];
181 }
182
183 - (BOOL) nameExistsInFolder: (NSString *) objectName
184 {
185   NSArray *fields, *records;
186   EOQualifier *qualifier;
187
188   qualifier
189     = [EOQualifier qualifierWithQualifierFormat:
190                      [NSString stringWithFormat: @"c_name='%@'", objectName]];
191
192   fields = [NSArray arrayWithObject: @"c_name"];
193   records = [[self ocsFolder] fetchFields: fields
194                               matchingQualifier: qualifier];
195   return (records
196           && ![records isKindOfClass:[NSException class]]
197           && [records count] > 0);
198 }
199
200 - (NSDictionary *)fetchContentStringsAndNamesOfAllObjects {
201   NSDictionary *files;
202   
203   files = [[self ocsFolder] fetchContentsOfAllFiles];
204   if (![files isNotNull]) {
205     [self errorWithFormat:@"(%s): fetch failed!", __PRETTY_FUNCTION__];
206     return nil;
207   }
208   if ([files isKindOfClass:[NSException class]])
209     return files;
210   return files;
211 }
212
213 /* reflection */
214
215 - (NSString *)defaultFilenameExtension {
216   /* 
217      Override to add an extension to a filename
218      
219      Note: be careful with that, needs to be consistent with object lookup!
220   */
221   return nil;
222 }
223
224 - (NSArray *) davResourceType
225 {
226   NSArray *rType, *groupDavCollection;
227
228   if ([self respondsToSelector: @selector (groupDavResourceType)])
229     {
230       groupDavCollection = [NSArray arrayWithObjects: [self groupDavResourceType],
231                                     @"http://groupdav.org/", @"G", nil];
232       rType = [NSArray arrayWithObjects: @"collection", groupDavCollection, nil];
233     }
234   else
235     rType = [NSArray arrayWithObject: @"collection"];
236
237   return rType;
238 }
239
240 - (NSArray *) toOneRelationshipKeys {
241   /* toOneRelationshipKeys are the 'files' contained in a folder */
242   NSMutableArray *ma;
243   NSArray  *names;
244   NSString *name, *ext;
245   unsigned i, count;
246   NSRange  r;
247
248   names = [self fetchContentObjectNames];
249   count = [names count];
250   ext = [self defaultFilenameExtension];
251   if (count && [ext length] > 0)
252     {
253       ma = [NSMutableArray arrayWithCapacity: count];
254       for (i = 0; i < count; i++)
255         {
256           name = [names objectAtIndex: i];
257           r = [name rangeOfString: @"."];
258           if (r.length == 0)
259             name = [[name stringByAppendingString:@"."] stringByAppendingString: ext];
260           [ma addObject:name];
261         }
262
263       names = ma;
264     }
265
266   return names;
267 }
268
269 /* acls as a container */
270
271 - (NSArray *) aclsForObjectAtPath: (NSArray *) objectPathArray;
272 {
273   EOQualifier *qualifier;
274   NSString *qs;
275
276   qs = [NSString stringWithFormat: @"c_object = '/%@'",
277                  [objectPathArray componentsJoinedByString: @"/"]];
278   qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
279
280   return [[self ocsFolder] fetchAclMatchingQualifier: qualifier];
281 }
282
283 - (NSArray *) _fetchAclsForUser: (NSString *) uid
284                 forObjectAtPath: (NSString *) objectPath
285 {
286   EOQualifier *qualifier;
287   NSArray *records;
288   NSMutableArray *acls;
289   NSString *qs;
290
291   qs = [NSString stringWithFormat: @"(c_object = '/%@') AND (c_uid = '%@')",
292                  objectPath, uid];
293   qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
294   records = [[self ocsFolder] fetchAclMatchingQualifier: qualifier];
295
296   acls = [NSMutableArray array];
297   if ([records count] > 0)
298     {
299       [acls addObject: SOGoRole_AuthorizedSubscriber];
300       [acls addObjectsFromArray: [records valueForKey: @"c_role"]];
301     }
302
303   return acls;
304 }
305
306 - (void) _cacheRoles: (NSArray *) roles
307              forUser: (NSString *) uid
308      forObjectAtPath: (NSString *) objectPath
309 {
310   NSMutableDictionary *aclsForObject;
311
312   aclsForObject = [aclCache objectForKey: objectPath];
313   if (!aclsForObject)
314     {
315       aclsForObject = [NSMutableDictionary dictionary];
316       [aclCache setObject: aclsForObject
317                 forKey: objectPath];
318     }
319   if (roles)
320     [aclsForObject setObject: roles forKey: uid];
321   else
322     [aclsForObject removeObjectForKey: uid];
323 }
324
325 - (NSArray *) aclsForUser: (NSString *) uid
326           forObjectAtPath: (NSArray *) objectPathArray
327 {
328   NSArray *acls;
329   NSString *objectPath;
330   NSDictionary *aclsForObject;
331
332   objectPath = [objectPathArray componentsJoinedByString: @"/"];
333   aclsForObject = [aclCache objectForKey: objectPath];
334   if (aclsForObject)
335     acls = [aclsForObject objectForKey: uid];
336   else
337     acls = nil;
338   if (!acls)
339     {
340       acls = [self _fetchAclsForUser: uid forObjectAtPath: objectPath];
341       [self _cacheRoles: acls forUser: uid forObjectAtPath: objectPath];
342     }
343
344   if (!([acls count] || [uid isEqualToString: defaultUser]))
345     acls = [self aclsForUser: defaultUser forObjectAtPath: objectPathArray];
346
347   return acls;
348 }
349
350 - (void) removeAclsForUsers: (NSArray *) users
351             forObjectAtPath: (NSArray *) objectPathArray
352 {
353   EOQualifier *qualifier;
354   NSString *uids, *qs, *objectPath;
355   NSMutableDictionary *aclsForObject;
356
357   if ([users count] > 0)
358     {
359       objectPath = [objectPathArray componentsJoinedByString: @"/"];
360       aclsForObject = [aclCache objectForKey: objectPath];
361       if (aclsForObject)
362         [aclsForObject removeObjectsForKeys: users];
363       uids = [users componentsJoinedByString: @"') OR (c_uid = '"];
364       qs = [NSString
365              stringWithFormat: @"(c_object = '/%@') AND ((c_uid = '%@'))",
366              objectPath, uids];
367       qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
368       [[self ocsFolder] deleteAclMatchingQualifier: qualifier];
369     }
370 }
371
372 - (void) setRoles: (NSArray *) roles
373           forUser: (NSString *) uid
374   forObjectAtPath: (NSArray *) objectPathArray
375 {
376   EOAdaptorChannel *channel;
377   GCSFolder *folder;
378   NSEnumerator *userRoles;
379   NSString *SQL, *currentRole, *objectPath;
380
381   [self removeAclsForUsers: [NSArray arrayWithObject: uid]
382         forObjectAtPath: objectPathArray];
383   objectPath = [objectPathArray componentsJoinedByString: @"/"];
384   [self _cacheRoles: roles forUser: uid forObjectAtPath: objectPath];
385
386   folder = [self ocsFolder];
387   channel = [folder acquireAclChannel];
388   userRoles = [roles objectEnumerator];
389   currentRole = [userRoles nextObject];
390   while (currentRole)
391     {
392       if (![currentRole isEqualToString: SOGoRole_AuthorizedSubscriber])
393         {
394           SQL = [NSString stringWithFormat: @"INSERT INTO %@"
395                           @" (c_object, c_uid, c_role)"
396                           @" VALUES ('/%@', '%@', '%@')",
397                           [folder aclTableName],
398                           objectPath, uid, currentRole];
399           [channel evaluateExpressionX: SQL];
400         }
401       currentRole = [userRoles nextObject];
402     }
403
404   [folder releaseChannel: channel];
405 }
406
407 /* acls */
408 - (NSArray *) defaultAclRoles
409 {
410 #warning this should be changed to something useful
411   return nil;
412 }
413
414 - (NSArray *) acls
415 {
416   return [self aclsForObjectAtPath: [self pathArrayToSoObject]];
417 }
418
419 - (NSArray *) aclsForUser: (NSString *) uid
420 {
421   return [self aclsForUser: uid
422                forObjectAtPath: [self pathArrayToSoObject]];
423 }
424
425 - (void) setRoles: (NSArray *) roles
426           forUser: (NSString *) uid
427 {
428   return [self setRoles: roles
429                forUser: uid
430                forObjectAtPath: [self pathArrayToSoObject]];
431 }
432
433 - (void) removeAclsForUsers: (NSArray *) users
434 {
435   return [self removeAclsForUsers: users
436                forObjectAtPath: [self pathArrayToSoObject]];
437 }
438
439 /* WebDAV */
440
441 - (BOOL) davIsCollection
442 {
443   return [self isFolderish];
444 }
445
446 /* folder type */
447
448 - (NSString *)outlookFolderClass {
449   return nil;
450 }
451
452 /* description */
453
454 - (void)appendAttributesToDescription:(NSMutableString *)_ms {
455   [super appendAttributesToDescription:_ms];
456   
457   [_ms appendFormat:@" ocs=%@", [self ocsPath]];
458 }
459
460 - (NSString *)loggingPrefix {
461   return [NSString stringWithFormat:@"<0x%08X[%@]:%@>",
462                    self, NSStringFromClass([self class]),
463                    [self nameInContainer]];
464 }
465
466 @end /* SOGoFolder */