]> err.no Git - scalable-opengroupware.org/blob - SoObjects/SOGo/SOGoFolder.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1092 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 - (BOOL) folderIsMandatory
145 {
146   [self subclassResponsibility: _cmd];
147
148   return NO;
149 }
150
151 - (GCSFolder *) ocsFolder
152 {
153   GCSFolder *folder;
154
155   if (!ocsFolder)
156     {
157       ocsFolder = [self ocsFolderForPath: [self ocsPath]];
158       if (!ocsFolder
159           && [self folderIsMandatory]
160           && [self create])
161         ocsFolder = [self ocsFolderForPath: [self ocsPath]];
162       [ocsFolder retain];
163     }
164
165   if ([ocsFolder isNotNull])
166     folder = ocsFolder;
167   else
168     folder = nil;
169
170   return folder;
171 }
172
173 - (NSString *) folderType
174 {
175   return @"";
176 }
177
178 - (BOOL) create
179 {
180   NSException *result;
181
182   result = [[self folderManager] createFolderOfType: [self folderType]
183                                  atPath: ocsPath];
184
185   return (result == nil);
186 }
187
188 - (NSException *) delete
189 {
190   return [[self folderManager] deleteFolderAtPath: ocsPath];
191 }
192
193 - (NSArray *) fetchContentObjectNames
194 {
195   NSArray *fields, *records;
196   
197   fields = [NSArray arrayWithObject: @"c_name"];
198   records = [[self ocsFolder] fetchFields:fields matchingQualifier:nil];
199   if (![records isNotNull])
200     {
201       [self errorWithFormat: @"(%s): fetch failed!", __PRETTY_FUNCTION__];
202       return nil;
203     }
204   if ([records isKindOfClass: [NSException class]])
205     return records;
206   return [records valueForKey: @"c_name"];
207 }
208
209 - (BOOL) nameExistsInFolder: (NSString *) objectName
210 {
211   NSArray *fields, *records;
212   EOQualifier *qualifier;
213
214   qualifier
215     = [EOQualifier qualifierWithQualifierFormat:
216                      [NSString stringWithFormat: @"c_name='%@'", objectName]];
217
218   fields = [NSArray arrayWithObject: @"c_name"];
219   records = [[self ocsFolder] fetchFields: fields
220                               matchingQualifier: qualifier];
221   return (records
222           && ![records isKindOfClass:[NSException class]]
223           && [records count] > 0);
224 }
225
226 - (NSDictionary *) fetchContentStringsAndNamesOfAllObjects
227 {
228   NSDictionary *files;
229   
230   files = [[self ocsFolder] fetchContentsOfAllFiles];
231   if (![files isNotNull])
232     {
233       [self errorWithFormat:@"(%s): fetch failed!", __PRETTY_FUNCTION__];
234       return nil;
235     }
236   if ([files isKindOfClass:[NSException class]])
237     return files;
238   return files;
239 }
240
241 /* reflection */
242
243 - (NSString *) defaultFilenameExtension
244 {
245   /* 
246      Override to add an extension to a filename
247      
248      Note: be careful with that, needs to be consistent with object lookup!
249   */
250   return nil;
251 }
252
253 - (NSArray *) davResourceType
254 {
255   NSArray *rType, *groupDavCollection;
256
257   if ([self respondsToSelector: @selector (groupDavResourceType)])
258     {
259       groupDavCollection = [NSArray arrayWithObjects: [self groupDavResourceType],
260                                     XMLNS_GROUPDAV, nil];
261       rType = [NSArray arrayWithObjects: @"collection", groupDavCollection, nil];
262     }
263   else
264     rType = [NSArray arrayWithObject: @"collection"];
265
266   return rType;
267 }
268
269 - (NSString *) davContentType
270 {
271   return @"httpd/unix-directory";
272 }
273
274 - (NSArray *) toOneRelationshipKeys
275 {
276   /* toOneRelationshipKeys are the 'files' contained in a folder */
277   NSMutableArray *ma;
278   NSArray  *names;
279   NSString *name, *ext;
280   unsigned i, count;
281   NSRange  r;
282
283   names = [self fetchContentObjectNames];
284   count = [names count];
285   ext = [self defaultFilenameExtension];
286   if (count && [ext length] > 0)
287     {
288       ma = [NSMutableArray arrayWithCapacity: count];
289       for (i = 0; i < count; i++)
290         {
291           name = [names objectAtIndex: i];
292           r = [name rangeOfString: @"."];
293           if (r.length == 0)
294             name = [[name stringByAppendingString:@"."] stringByAppendingString: ext];
295           [ma addObject:name];
296         }
297
298       names = ma;
299     }
300
301   return names;
302 }
303
304 /* acls as a container */
305
306 - (NSArray *) aclUsersForObjectAtPath: (NSArray *) objectPathArray;
307 {
308   EOQualifier *qualifier;
309   NSString *qs;
310   NSArray *records;
311
312   qs = [NSString stringWithFormat: @"c_object = '/%@'",
313                  [objectPathArray componentsJoinedByString: @"/"]];
314   qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
315   records = [[self ocsFolder] fetchAclMatchingQualifier: qualifier];
316
317   return [records valueForKey: @"c_uid"];
318 }
319
320 - (NSArray *) _fetchAclsForUser: (NSString *) uid
321                 forObjectAtPath: (NSString *) objectPath
322 {
323   EOQualifier *qualifier;
324   NSArray *records;
325   NSMutableArray *acls;
326   NSString *qs;
327
328   qs = [NSString stringWithFormat: @"(c_object = '/%@') AND (c_uid = '%@')",
329                  objectPath, uid];
330   qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
331   records = [[self ocsFolder] fetchAclMatchingQualifier: qualifier];
332
333   acls = [NSMutableArray array];
334   if ([records count] > 0)
335     {
336       [acls addObject: SOGoRole_AuthorizedSubscriber];
337       [acls addObjectsFromArray: [records valueForKey: @"c_role"]];
338     }
339
340   return acls;
341 }
342
343 - (void) _cacheRoles: (NSArray *) roles
344              forUser: (NSString *) uid
345      forObjectAtPath: (NSString *) objectPath
346 {
347   NSMutableDictionary *aclsForObject;
348
349   aclsForObject = [aclCache objectForKey: objectPath];
350   if (!aclsForObject)
351     {
352       aclsForObject = [NSMutableDictionary dictionary];
353       [aclCache setObject: aclsForObject
354                 forKey: objectPath];
355     }
356   if (roles)
357     [aclsForObject setObject: roles forKey: uid];
358   else
359     [aclsForObject removeObjectForKey: uid];
360 }
361
362 - (NSArray *) aclsForUser: (NSString *) uid
363           forObjectAtPath: (NSArray *) objectPathArray
364 {
365   NSArray *acls;
366   NSString *objectPath;
367   NSDictionary *aclsForObject;
368
369   objectPath = [objectPathArray componentsJoinedByString: @"/"];
370   aclsForObject = [aclCache objectForKey: objectPath];
371   if (aclsForObject)
372     acls = [aclsForObject objectForKey: uid];
373   else
374     acls = nil;
375   if (!acls)
376     {
377       acls = [self _fetchAclsForUser: uid forObjectAtPath: objectPath];
378       [self _cacheRoles: acls forUser: uid forObjectAtPath: objectPath];
379     }
380
381   if (!([acls count] || [uid isEqualToString: defaultUserID]))
382     acls = [self aclsForUser: defaultUserID
383                  forObjectAtPath: objectPathArray];
384
385   return acls;
386 }
387
388 - (void) removeAclsForUsers: (NSArray *) users
389             forObjectAtPath: (NSArray *) objectPathArray
390 {
391   EOQualifier *qualifier;
392   NSString *uids, *qs, *objectPath;
393   NSMutableDictionary *aclsForObject;
394
395   if ([users count] > 0)
396     {
397       objectPath = [objectPathArray componentsJoinedByString: @"/"];
398       aclsForObject = [aclCache objectForKey: objectPath];
399       if (aclsForObject)
400         [aclsForObject removeObjectsForKeys: users];
401       uids = [users componentsJoinedByString: @"') OR (c_uid = '"];
402       qs = [NSString
403              stringWithFormat: @"(c_object = '/%@') AND ((c_uid = '%@'))",
404              objectPath, uids];
405       qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
406       [[self ocsFolder] deleteAclMatchingQualifier: qualifier];
407     }
408 }
409
410 - (void) _commitRoles: (NSArray *) roles
411                forUID: (NSString *) uid
412             forObject: (NSString *) objectPath
413 {
414   EOAdaptorChannel *channel;
415   GCSFolder *folder;
416   NSEnumerator *userRoles;
417   NSString *SQL, *currentRole;
418
419   folder = [self ocsFolder];
420   channel = [folder acquireAclChannel];
421   userRoles = [roles objectEnumerator];
422   currentRole = [userRoles nextObject];
423   while (currentRole)
424     {
425       SQL = [NSString stringWithFormat: @"INSERT INTO %@"
426                       @" (c_object, c_uid, c_role)"
427                       @" VALUES ('/%@', '%@', '%@')",
428                       [folder aclTableName],
429                       objectPath, uid, currentRole];
430       [channel evaluateExpressionX: SQL];
431       currentRole = [userRoles nextObject];
432     }
433
434   [folder releaseChannel: channel];
435 }
436
437 - (void) setRoles: (NSArray *) roles
438           forUser: (NSString *) uid
439   forObjectAtPath: (NSArray *) objectPathArray
440 {
441   NSString *objectPath;
442   NSMutableArray *newRoles;
443
444   [self removeAclsForUsers: [NSArray arrayWithObject: uid]
445         forObjectAtPath: objectPathArray];
446
447   newRoles = [NSMutableArray arrayWithArray: roles];
448   [newRoles removeObject: SOGoRole_AuthorizedSubscriber];
449   [newRoles removeObject: SOGoRole_None];
450   objectPath = [objectPathArray componentsJoinedByString: @"/"];
451   [self _cacheRoles: newRoles forUser: uid
452         forObjectAtPath: objectPath];
453   if (![newRoles count])
454     [newRoles addObject: SOGoRole_None];
455
456   [self _commitRoles: newRoles forUID: uid forObject: objectPath];
457 }
458
459 /* acls */
460 - (NSArray *) aclUsers
461 {
462   return [self aclUsersForObjectAtPath: [self pathArrayToSoObject]];
463 }
464
465 - (NSArray *) aclsForUser: (NSString *) uid
466 {
467   NSMutableArray *acls;
468   NSArray *ownAcls, *containerAcls;
469
470   acls = [NSMutableArray array];
471   ownAcls = [self aclsForUser: uid
472                   forObjectAtPath: [self pathArrayToSoObject]];
473   [acls addObjectsFromArray: ownAcls];
474   if ([container respondsToSelector: @selector (aclsForUser:)])
475     {
476       containerAcls = [container aclsForUser: uid];
477       if ([containerAcls count] > 0)
478         {
479           if ([containerAcls containsObject: SOGoRole_ObjectReader])
480             [acls addObject: SOGoRole_ObjectViewer];
481 #warning this should be checked
482           if ([containerAcls containsObject: SOGoRole_ObjectEraser])
483             [acls addObject: SOGoRole_ObjectEraser];
484         }
485     }
486
487   return acls;
488 }
489
490 - (void) setRoles: (NSArray *) roles
491           forUser: (NSString *) uid
492 {
493   return [self setRoles: roles
494                forUser: uid
495                forObjectAtPath: [self pathArrayToSoObject]];
496 }
497
498 - (void) removeAclsForUsers: (NSArray *) users
499 {
500   return [self removeAclsForUsers: users
501                forObjectAtPath: [self pathArrayToSoObject]];
502 }
503
504 - (NSString *) defaultUserID
505 {
506   return defaultUserID;
507 }
508
509 - (NSString *) httpURLForAdvisoryToUser: (NSString *) uid
510 {
511   return [[self soURL] absoluteString];
512 }
513
514 - (NSString *) resourceURLForAdvisoryToUser: (NSString *) uid
515 {
516   return [[self davURL] absoluteString];
517 }
518
519 /* WebDAV */
520
521 - (BOOL) davIsCollection
522 {
523   return [self isFolderish];
524 }
525
526 /* folder type */
527
528 - (NSString *)outlookFolderClass {
529   return nil;
530 }
531
532 /* description */
533
534 - (void)appendAttributesToDescription:(NSMutableString *)_ms {
535   [super appendAttributesToDescription:_ms];
536   
537   [_ms appendFormat:@" ocs=%@", [self ocsPath]];
538 }
539
540 - (NSString *)loggingPrefix {
541   return [NSString stringWithFormat:@"<0x%08X[%@]:%@>",
542                    self, NSStringFromClass([self class]),
543                    [self nameInContainer]];
544 }
545
546 @end /* SOGoFolder */