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