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