02111-1307, USA.
*/
+#import <unistd.h>
+#import <stdlib.h>
+
+#import <Foundation/NSArray.h>
+#import <Foundation/NSDictionary.h>
+#import <Foundation/NSException.h>
+#import <Foundation/NSURL.h>
+
#import <NGObjWeb/SoObject.h>
+#import <NGExtensions/NSNull+misc.h>
+#import <NGExtensions/NSObject+Logs.h>
+#import <EOControl/EOQualifier.h>
+#import <GDLAccess/EOAdaptorChannel.h>
#import <GDLContentStore/GCSFolderManager.h>
#import <GDLContentStore/GCSFolder.h>
#import <GDLContentStore/GCSFolderType.h>
+#import <SaxObjC/XMLNamespaces.h>
+
+#import "SOGoPermissions.h"
#import "SOGoFolder.h"
-#import "common.h"
-#import <unistd.h>
-#import <stdlib.h>
-#import "SOGoAclsFolder.h"
+static NSString *defaultUserID = @"<default>";
@implementation SOGoFolder
-+ (int)version {
++ (int) version
+{
return [super version] + 0 /* v0 */;
}
-+ (void)initialize {
+
++ (void) initialize
+{
NSAssert2([super version] == 0,
@"invalid superclass (%@) version %i !",
NSStringFromClass([self superclass]), [super version]);
}
-+ (NSString *)globallyUniqueObjectId {
++ (NSString *) globallyUniqueObjectId
+{
/*
4C08AE1A-A808-11D8-AC5A-000393BBAFF6
SOGo-Web-28273-18283-288182
printf( "%x", *(int *) &f);
*/
- static int pid = 0;
- static int sequence = 0;
+ static int pid = 0;
+ static int sequence = 0;
static float rndm = 0;
float f;
- if (pid == 0) { /* break if we fork ;-) */
- pid = getpid();
- rndm = random();
- }
+ if (pid == 0)
+ { /* break if we fork ;-) */
+ pid = getpid();
+ rndm = random();
+ }
sequence++;
f = [[NSDate date] timeIntervalSince1970];
return [NSString stringWithFormat:@"%0X-%0X-%0X-%0X",
pid, *(int *)&f, sequence++, random];
}
-- (void)dealloc {
- [self->ocsFolder release];
- [self->ocsPath release];
+- (id) init
+{
+ if ((self = [super init]))
+ {
+ ocsPath = nil;
+ ocsFolder = nil;
+ aclCache = [NSMutableDictionary new];
+ }
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [ocsFolder release];
+ [ocsPath release];
+ [aclCache release];
[super dealloc];
}
/* accessors */
-- (BOOL)isFolderish {
+- (BOOL) isFolderish
+{
return YES;
}
-- (void)setOCSPath:(NSString *)_path {
- if ([self->ocsPath isEqualToString:_path])
+- (void) setOCSPath: (NSString *) _path
+{
+ if ([ocsPath isEqualToString:_path])
return;
- if (self->ocsPath)
+ if (ocsPath)
[self warnWithFormat:@"GCS path is already set! '%@'", _path];
- ASSIGNCOPY(self->ocsPath, _path);
+ ASSIGNCOPY(ocsPath, _path);
}
-- (NSString *)ocsPath {
- return self->ocsPath;
+- (NSString *) ocsPath
+{
+ return ocsPath;
}
-- (GCSFolderManager *)folderManager {
+- (GCSFolderManager *) folderManager
+{
static GCSFolderManager *folderManager = nil;
if (!folderManager)
return folderManager;
}
-- (GCSFolder *)ocsFolderForPath:(NSString *)_path {
+- (GCSFolder *) ocsFolderForPath: (NSString *) _path
+{
return [[self folderManager] folderAtPath:_path];
}
-- (GCSFolder *) ocsFolder {
+- (GCSFolder *) ocsFolder
+{
GCSFolder *folder;
if (!ocsFolder)
if ([self respondsToSelector: @selector (groupDavResourceType)])
{
groupDavCollection = [NSArray arrayWithObjects: [self groupDavResourceType],
- @"http://groupdav.org/", @"G", nil];
+ XMLNS_GROUPDAV, nil];
rType = [NSArray arrayWithObjects: @"collection", groupDavCollection, nil];
}
else
return rType;
}
+- (NSString *) davContentType
+{
+ return @"httpd/unix-directory";
+}
+
- (NSArray *) toOneRelationshipKeys {
/* toOneRelationshipKeys are the 'files' contained in a folder */
NSMutableArray *ma;
return names;
}
+/* acls as a container */
+
+- (NSArray *) aclUsersForObjectAtPath: (NSArray *) objectPathArray;
+{
+ EOQualifier *qualifier;
+ NSString *qs;
+ NSArray *records;
+
+ qs = [NSString stringWithFormat: @"c_object = '/%@'",
+ [objectPathArray componentsJoinedByString: @"/"]];
+ qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
+ records = [[self ocsFolder] fetchAclMatchingQualifier: qualifier];
+
+ return [records valueForKey: @"c_uid"];
+}
+
+- (NSArray *) _fetchAclsForUser: (NSString *) uid
+ forObjectAtPath: (NSString *) objectPath
+{
+ EOQualifier *qualifier;
+ NSArray *records;
+ NSMutableArray *acls;
+ NSString *qs;
+
+ qs = [NSString stringWithFormat: @"(c_object = '/%@') AND (c_uid = '%@')",
+ objectPath, uid];
+ qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
+ records = [[self ocsFolder] fetchAclMatchingQualifier: qualifier];
+
+ acls = [NSMutableArray array];
+ if ([records count] > 0)
+ {
+ [acls addObject: SOGoRole_AuthorizedSubscriber];
+ [acls addObjectsFromArray: [records valueForKey: @"c_role"]];
+ }
+
+ return acls;
+}
+
+- (void) _cacheRoles: (NSArray *) roles
+ forUser: (NSString *) uid
+ forObjectAtPath: (NSString *) objectPath
+{
+ NSMutableDictionary *aclsForObject;
+
+ aclsForObject = [aclCache objectForKey: objectPath];
+ if (!aclsForObject)
+ {
+ aclsForObject = [NSMutableDictionary dictionary];
+ [aclCache setObject: aclsForObject
+ forKey: objectPath];
+ }
+ if (roles)
+ [aclsForObject setObject: roles forKey: uid];
+ else
+ [aclsForObject removeObjectForKey: uid];
+}
+
+- (NSArray *) aclsForUser: (NSString *) uid
+ forObjectAtPath: (NSArray *) objectPathArray
+{
+ NSArray *acls;
+ NSString *objectPath;
+ NSDictionary *aclsForObject;
+
+ objectPath = [objectPathArray componentsJoinedByString: @"/"];
+ aclsForObject = [aclCache objectForKey: objectPath];
+ if (aclsForObject)
+ acls = [aclsForObject objectForKey: uid];
+ else
+ acls = nil;
+ if (!acls)
+ {
+ acls = [self _fetchAclsForUser: uid forObjectAtPath: objectPath];
+ [self _cacheRoles: acls forUser: uid forObjectAtPath: objectPath];
+ }
+
+ if (!([acls count] || [uid isEqualToString: defaultUserID]))
+ acls = [self aclsForUser: defaultUserID
+ forObjectAtPath: objectPathArray];
+
+ return acls;
+}
+
+- (void) removeAclsForUsers: (NSArray *) users
+ forObjectAtPath: (NSArray *) objectPathArray
+{
+ EOQualifier *qualifier;
+ NSString *uids, *qs, *objectPath;
+ NSMutableDictionary *aclsForObject;
+
+ if ([users count] > 0)
+ {
+ objectPath = [objectPathArray componentsJoinedByString: @"/"];
+ aclsForObject = [aclCache objectForKey: objectPath];
+ if (aclsForObject)
+ [aclsForObject removeObjectsForKeys: users];
+ uids = [users componentsJoinedByString: @"') OR (c_uid = '"];
+ qs = [NSString
+ stringWithFormat: @"(c_object = '/%@') AND ((c_uid = '%@'))",
+ objectPath, uids];
+ qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
+ [[self ocsFolder] deleteAclMatchingQualifier: qualifier];
+ }
+}
+
+- (void) _commitRoles: (NSArray *) roles
+ forUID: (NSString *) uid
+ forObject: (NSString *) objectPath
+{
+ EOAdaptorChannel *channel;
+ GCSFolder *folder;
+ NSEnumerator *userRoles;
+ NSString *SQL, *currentRole;
+
+ folder = [self ocsFolder];
+ channel = [folder acquireAclChannel];
+ userRoles = [roles objectEnumerator];
+ currentRole = [userRoles nextObject];
+ while (currentRole)
+ {
+ SQL = [NSString stringWithFormat: @"INSERT INTO %@"
+ @" (c_object, c_uid, c_role)"
+ @" VALUES ('/%@', '%@', '%@')",
+ [folder aclTableName],
+ objectPath, uid, currentRole];
+ [channel evaluateExpressionX: SQL];
+ currentRole = [userRoles nextObject];
+ }
+
+ [folder releaseChannel: channel];
+}
+
+- (void) setRoles: (NSArray *) roles
+ forUser: (NSString *) uid
+ forObjectAtPath: (NSArray *) objectPathArray
+{
+ NSString *objectPath;
+ NSMutableArray *newRoles;
+
+ [self removeAclsForUsers: [NSArray arrayWithObject: uid]
+ forObjectAtPath: objectPathArray];
+
+ newRoles = [NSMutableArray arrayWithArray: roles];
+ [newRoles removeObject: SOGoRole_AuthorizedSubscriber];
+ [newRoles removeObject: SOGoRole_None];
+ objectPath = [objectPathArray componentsJoinedByString: @"/"];
+ [self _cacheRoles: newRoles forUser: uid
+ forObjectAtPath: objectPath];
+ if (![newRoles count])
+ [newRoles addObject: SOGoRole_None];
+
+ [self _commitRoles: newRoles forUID: uid forObject: objectPath];
+}
+
+/* acls */
+- (NSArray *) aclUsers
+{
+ return [self aclUsersForObjectAtPath: [self pathArrayToSoObject]];
+}
+
+- (NSArray *) aclsForUser: (NSString *) uid
+{
+ NSMutableArray *acls;
+ NSArray *ownAcls, *containerAcls;
+
+ acls = [NSMutableArray array];
+ ownAcls = [self aclsForUser: uid
+ forObjectAtPath: [self pathArrayToSoObject]];
+ [acls addObjectsFromArray: ownAcls];
+ if ([container respondsToSelector: @selector (aclsForUser:)])
+ {
+ containerAcls = [container aclsForUser: uid];
+ if ([containerAcls count] > 0)
+ {
+ if ([containerAcls containsObject: SOGoRole_ObjectReader])
+ [acls addObject: SOGoRole_ObjectViewer];
+#warning this should be checked
+ if ([containerAcls containsObject: SOGoRole_ObjectEraser])
+ [acls addObject: SOGoRole_ObjectEraser];
+ }
+ }
+
+ return acls;
+}
+
+- (void) setRoles: (NSArray *) roles
+ forUser: (NSString *) uid
+{
+ return [self setRoles: roles
+ forUser: uid
+ forObjectAtPath: [self pathArrayToSoObject]];
+}
+
+- (void) removeAclsForUsers: (NSArray *) users
+{
+ return [self removeAclsForUsers: users
+ forObjectAtPath: [self pathArrayToSoObject]];
+}
+
+- (NSString *) defaultUserID
+{
+ return defaultUserID;
+}
+
+- (NSString *) httpURLForAdvisoryToUser: (NSString *) uid
+{
+ return [[self soURL] absoluteString];
+}
+
+- (NSString *) resourceURLForAdvisoryToUser: (NSString *) uid
+{
+ return [[self davURL] absoluteString];
+}
+
/* WebDAV */
-- (BOOL)davIsCollection {
+- (BOOL) davIsCollection
+{
return [self isFolderish];
}