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