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