]> err.no Git - scalable-opengroupware.org/blob - SoObjects/SOGo/SOGoFolder.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1068 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 *defaultUserID = @"<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 *) aclUsersForObjectAtPath: (NSArray *) objectPathArray;
272 {
273   EOQualifier *qualifier;
274   NSString *qs;
275   NSArray *records;
276
277   qs = [NSString stringWithFormat: @"c_object = '/%@'",
278                  [objectPathArray componentsJoinedByString: @"/"]];
279   qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
280   records = [[self ocsFolder] fetchAclMatchingQualifier: qualifier];
281
282   return [records valueForKey: @"c_uid"];
283 }
284
285 - (NSArray *) _fetchAclsForUser: (NSString *) uid
286                 forObjectAtPath: (NSString *) objectPath
287 {
288   EOQualifier *qualifier;
289   NSArray *records;
290   NSMutableArray *acls;
291   NSString *qs;
292
293   qs = [NSString stringWithFormat: @"(c_object = '/%@') AND (c_uid = '%@')",
294                  objectPath, uid];
295   qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
296   records = [[self ocsFolder] fetchAclMatchingQualifier: qualifier];
297
298   acls = [NSMutableArray array];
299   if ([records count] > 0)
300     {
301       [acls addObject: SOGoRole_AuthorizedSubscriber];
302       [acls addObjectsFromArray: [records valueForKey: @"c_role"]];
303     }
304
305   return acls;
306 }
307
308 - (void) _cacheRoles: (NSArray *) roles
309              forUser: (NSString *) uid
310      forObjectAtPath: (NSString *) objectPath
311 {
312   NSMutableDictionary *aclsForObject;
313
314   aclsForObject = [aclCache objectForKey: objectPath];
315   if (!aclsForObject)
316     {
317       aclsForObject = [NSMutableDictionary dictionary];
318       [aclCache setObject: aclsForObject
319                 forKey: objectPath];
320     }
321   if (roles)
322     [aclsForObject setObject: roles forKey: uid];
323   else
324     [aclsForObject removeObjectForKey: uid];
325 }
326
327 - (NSArray *) aclsForUser: (NSString *) uid
328           forObjectAtPath: (NSArray *) objectPathArray
329 {
330   NSArray *acls;
331   NSString *objectPath;
332   NSDictionary *aclsForObject;
333
334   objectPath = [objectPathArray componentsJoinedByString: @"/"];
335   aclsForObject = [aclCache objectForKey: objectPath];
336   if (aclsForObject)
337     acls = [aclsForObject objectForKey: uid];
338   else
339     acls = nil;
340   if (!acls)
341     {
342       acls = [self _fetchAclsForUser: uid forObjectAtPath: objectPath];
343       [self _cacheRoles: acls forUser: uid forObjectAtPath: objectPath];
344     }
345
346   if (!([acls count] || [uid isEqualToString: defaultUserID]))
347     acls = [self aclsForUser: defaultUserID
348                  forObjectAtPath: objectPathArray];
349
350   return acls;
351 }
352
353 - (void) removeAclsForUsers: (NSArray *) users
354             forObjectAtPath: (NSArray *) objectPathArray
355 {
356   EOQualifier *qualifier;
357   NSString *uids, *qs, *objectPath;
358   NSMutableDictionary *aclsForObject;
359
360   if ([users count] > 0)
361     {
362       objectPath = [objectPathArray componentsJoinedByString: @"/"];
363       aclsForObject = [aclCache objectForKey: objectPath];
364       if (aclsForObject)
365         [aclsForObject removeObjectsForKeys: users];
366       uids = [users componentsJoinedByString: @"') OR (c_uid = '"];
367       qs = [NSString
368              stringWithFormat: @"(c_object = '/%@') AND ((c_uid = '%@'))",
369              objectPath, uids];
370       qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
371       [[self ocsFolder] deleteAclMatchingQualifier: qualifier];
372     }
373 }
374
375 - (void) _commitRoles: (NSArray *) roles
376                forUID: (NSString *) uid
377             forObject: (NSString *) objectPath
378 {
379   EOAdaptorChannel *channel;
380   GCSFolder *folder;
381   NSEnumerator *userRoles;
382   NSString *SQL, *currentRole;
383
384   folder = [self ocsFolder];
385   channel = [folder acquireAclChannel];
386   userRoles = [roles objectEnumerator];
387   currentRole = [userRoles nextObject];
388   while (currentRole)
389     {
390       SQL = [NSString stringWithFormat: @"INSERT INTO %@"
391                       @" (c_object, c_uid, c_role)"
392                       @" VALUES ('/%@', '%@', '%@')",
393                       [folder aclTableName],
394                       objectPath, uid, currentRole];
395       [channel evaluateExpressionX: SQL];
396       currentRole = [userRoles nextObject];
397     }
398
399   [folder releaseChannel: channel];
400 }
401
402 - (void) setRoles: (NSArray *) roles
403           forUser: (NSString *) uid
404   forObjectAtPath: (NSArray *) objectPathArray
405 {
406   NSString *objectPath;
407   NSMutableArray *newRoles;
408
409   [self removeAclsForUsers: [NSArray arrayWithObject: uid]
410         forObjectAtPath: objectPathArray];
411
412   newRoles = [NSMutableArray arrayWithArray: roles];
413   [newRoles removeObject: SOGoRole_AuthorizedSubscriber];
414   [newRoles removeObject: SOGoRole_None];
415   objectPath = [objectPathArray componentsJoinedByString: @"/"];
416   [self _cacheRoles: newRoles forUser: uid
417         forObjectAtPath: objectPath];
418   if (![newRoles count])
419     [newRoles addObject: SOGoRole_None];
420
421   [self _commitRoles: newRoles forUID: uid forObject: objectPath];
422 }
423
424 /* acls */
425 - (NSArray *) aclUsers
426 {
427   return [self aclUsersForObjectAtPath: [self pathArrayToSoObject]];
428 }
429
430 - (NSArray *) aclsForUser: (NSString *) uid
431 {
432   return [self aclsForUser: uid
433                forObjectAtPath: [self pathArrayToSoObject]];
434 }
435
436 - (void) setRoles: (NSArray *) roles
437           forUser: (NSString *) uid
438 {
439   return [self setRoles: roles
440                forUser: uid
441                forObjectAtPath: [self pathArrayToSoObject]];
442 }
443
444 - (void) removeAclsForUsers: (NSArray *) users
445 {
446   return [self removeAclsForUsers: users
447                forObjectAtPath: [self pathArrayToSoObject]];
448 }
449
450 - (NSString *) defaultUserID
451 {
452   return defaultUserID;
453 }
454
455 - (BOOL) hasSupportForDefaultRoles
456 {
457   return YES;
458 }
459
460 /* WebDAV */
461
462 - (BOOL) davIsCollection
463 {
464   return [self isFolderish];
465 }
466
467 /* folder type */
468
469 - (NSString *)outlookFolderClass {
470   return nil;
471 }
472
473 /* description */
474
475 - (void)appendAttributesToDescription:(NSMutableString *)_ms {
476   [super appendAttributesToDescription:_ms];
477   
478   [_ms appendFormat:@" ocs=%@", [self ocsPath]];
479 }
480
481 - (NSString *)loggingPrefix {
482   return [NSString stringWithFormat:@"<0x%08X[%@]:%@>",
483                    self, NSStringFromClass([self class]),
484                    [self nameInContainer]];
485 }
486
487 @end /* SOGoFolder */