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