]> err.no Git - scalable-opengroupware.org/blobdiff - SoObjects/SOGo/SOGoFolder.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1173 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / SoObjects / SOGo / SOGoFolder.m
index d60a898150f3da079f7e1e3f5a44a9a89a48102d..a78d50740a1215711cac3a14350ab43c8ed83784 100644 (file)
   02111-1307, USA.
 */
 
+#import <Foundation/NSArray.h>
+#import <Foundation/NSDate.h>
+#import <Foundation/NSDictionary.h>
+#import <Foundation/NSException.h>
+#import <Foundation/NSKeyValueCoding.h>
+#import <Foundation/NSURL.h>
+
+#import <NGObjWeb/NSException+HTTP.h>
 #import <NGObjWeb/SoObject.h>
-
-#include "SOGoFolder.h"
-#include "common.h"
-#include <GDLContentStore/GCSFolderManager.h>
-#include <GDLContentStore/GCSFolder.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-#import "SOGoAclsFolder.h"
+#import <NGObjWeb/SoObject+SoDAV.h>
+#import <NGObjWeb/SoSelectorInvocation.h>
+#import <NGObjWeb/WOContext+SoObjects.h>
+#import <NGExtensions/NSNull+misc.h>
+#import <NGExtensions/NSObject+Logs.h>
+#import <EOControl/EOQualifier.h>
+#import <GDLAccess/EOAdaptorChannel.h>
+#import <GDLContentStore/GCSChannelManager.h>
+#import <GDLContentStore/GCSFolderManager.h>
+#import <GDLContentStore/GCSFolder.h>
+#import <GDLContentStore/GCSFolderType.h>
+#import <GDLContentStore/NSURL+GCS.h>
+#import <SaxObjC/XMLNamespaces.h>
+
+#import "NSArray+Utilities.h"
+#import "NSString+Utilities.h"
+
+#import "SOGoPermissions.h"
+#import "SOGoUser.h"
+
+#import "SOGoFolder.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 {
-  /*
-    4C08AE1A-A808-11D8-AC5A-000393BBAFF6
-    SOGo-Web-28273-18283-288182
-    printf( "%x", *(int *) &f);
-  */
-  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();
-  }
-  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) folderWithSubscriptionReference: (NSString *) reference
+                          inContainer: (id) aContainer
+{
+  id newFolder;
+  NSArray *elements, *pathElements;
+  NSString *ocsPath, *objectPath, *owner, *ocsName, *folderName;
+
+  elements = [reference componentsSeparatedByString: @":"];
+  owner = [elements objectAtIndex: 0];
+  objectPath = [elements objectAtIndex: 1];
+  pathElements = [objectPath componentsSeparatedByString: @"/"];
+  if ([pathElements count] > 1)
+    ocsName = [pathElements objectAtIndex: 1];
+  else
+    ocsName = @"personal";
+
+  ocsPath = [NSString stringWithFormat: @"/Users/%@/%@/%@",
+                     owner, [pathElements objectAtIndex: 0], ocsName];
+  folderName = [NSString stringWithFormat: @"%@_%@", owner, ocsName];
+  newFolder = [[self alloc] initWithName: folderName
+                           inContainer: aContainer];
+  [newFolder setOCSPath: ocsPath];
+  [newFolder setOwner: owner];
+
+  return newFolder;
+}
+
+- (id) init
+{
+  if ((self = [super init]))
+    {
+      displayName = nil;
+      ocsPath = nil;
+      ocsFolder = nil;
+      aclCache = [NSMutableDictionary new];
+    }
+
+  return self;
+}
+
+- (void) dealloc
+{
+  [ocsFolder release];
+  [ocsPath release];
+  [aclCache release];
+  [displayName release];
   [super dealloc];
 }
 
 /* accessors */
 
-- (BOOL)isFolderish {
+- (BOOL) isFolderish
+{
   return YES;
 }
 
-- (void)setOCSPath:(NSString *)_path {
-  if ([self->ocsPath isEqualToString:_path])
-    return;
-  
-  if (self->ocsPath)
-    [self warnWithFormat:@"GCS path is already set! '%@'", _path];
-  
-  ASSIGNCOPY(self->ocsPath, _path);
+- (void) setOCSPath: (NSString *) _path
+{
+  if (![ocsPath isEqualToString:_path])
+    {
+      if (ocsPath)
+       [self warnWithFormat: @"GCS path is already set! '%@'", _path];
+      ASSIGN (ocsPath, _path);
+    }
+}
+
+- (NSString *) ocsPath
+{
+  return ocsPath;
+}
+
+- (GCSFolderManager *) folderManager
+{
+  static GCSFolderManager *folderManager = nil;
+
+  if (!folderManager)
+    folderManager = [GCSFolderManager defaultFolderManager];
+
+  return folderManager;
 }
-- (NSString *)ocsPath {
-  return self->ocsPath;
+
+- (GCSFolder *) ocsFolderForPath: (NSString *) _path
+{
+  return [[self folderManager] folderAtPath: _path];
 }
 
-- (GCSFolderManager *)folderManager {
-  return [GCSFolderManager defaultFolderManager];
+- (BOOL) folderIsMandatory
+{
+  return [nameInContainer isEqualToString: @"personal"];
 }
 
-- (GCSFolder *)ocsFolderForPath:(NSString *)_path {
-  return [[self folderManager] folderAtPath:_path];
+- (void) _setDisplayNameFromRow: (NSDictionary *) row
+{
+  NSString *currentLogin, *ownerLogin;
+  NSDictionary *ownerIdentity;
+
+  displayName
+    = [NSMutableString stringWithString: [row objectForKey: @"c_foldername"]];
+  currentLogin = [[context activeUser] login];
+  ownerLogin = [self ownerInContext: context];
+  if (![currentLogin isEqualToString: ownerLogin])
+    {
+      ownerIdentity = [[SOGoUser userWithLogin: ownerLogin roles: nil]
+                       primaryIdentity];
+      [displayName appendFormat: @" (%@ <%@>)",
+                  [ownerIdentity objectForKey: @"fullName"],
+                  [ownerIdentity objectForKey: @"email"]];
+    }
+}
+
+- (void) _fetchDisplayName
+{
+  GCSChannelManager *cm;
+  EOAdaptorChannel *fc;
+  NSURL *folderLocation;
+  NSString *sql;
+  NSArray *attrs;
+  NSDictionary *row;
+
+  cm = [GCSChannelManager defaultChannelManager];
+  folderLocation
+    = [[GCSFolderManager defaultFolderManager] folderInfoLocation];
+  fc = [cm acquireOpenChannelForURL: folderLocation];
+  if (fc)
+    {
+      sql
+       = [NSString stringWithFormat: (@"SELECT c_foldername FROM %@"
+                                      @" WHERE c_path = '%@'"),
+                   [folderLocation gcsTableName], ocsPath];
+      [fc evaluateExpressionX: sql];
+      attrs = [fc describeResults: NO];
+      row = [fc fetchAttributes: attrs withZone: NULL];
+      if (row)
+       [self _setDisplayNameFromRow: row];
+      [fc cancelFetch];
+      [cm releaseChannel: fc];
+    }
 }
 
-- (GCSFolder *) ocsFolder {
+- (void) setDisplayName: (NSString *) newDisplayName
+{
+  ASSIGN (displayName, newDisplayName);
+}
+
+- (NSString *) displayName
+{
+  if (!displayName)
+    [self _fetchDisplayName];
+
+  return displayName;
+}
+
+- (NSString *) davDisplayName
+{
+  return [self displayName];
+}
+
+- (GCSFolder *) ocsFolder
+{
   GCSFolder *folder;
+  NSString *userLogin;
 
   if (!ocsFolder)
-    ocsFolder = [[self ocsFolderForPath:[self ocsPath]] retain];
+    {
+      ocsFolder = [self ocsFolderForPath: [self ocsPath]];
+      userLogin = [[context activeUser] login];
+      if (!ocsFolder
+         && [userLogin isEqualToString: [self ownerInContext: context]]
+         && [self folderIsMandatory]
+         && [self create])
+       ocsFolder = [self ocsFolderForPath: [self ocsPath]];
+      [ocsFolder retain];
+    }
 
   if ([ocsFolder isNotNull])
     folder = ocsFolder;
   return folder;
 }
 
-- (NSArray *)fetchContentObjectNames {
+- (NSString *) folderType
+{
+  return @"";
+}
+
+- (BOOL) create
+{
+  NSException *result;
+
+//   [self dieHard];
+  result = [[self folderManager] createFolderOfType: [self folderType]
+                                withName: displayName
+                                 atPath: ocsPath];
+
+  return (result == nil);
+}
+
+- (NSException *) delete
+{
+  NSException *error;
+
+  if ([nameInContainer isEqualToString: @"personal"])
+    error = [NSException exceptionWithHTTPStatus: 403
+                        reason: @"folder 'personal' cannot be deleted"];
+  else
+    error = [[self folderManager] deleteFolderAtPath: ocsPath];
+
+  return error;
+}
+
+- (void) renameTo: (NSString *) newName
+{
+  GCSChannelManager *cm;
+  EOAdaptorChannel *fc;
+  NSURL *folderLocation;
+  NSString *sql;
+
+  [displayName release];
+  displayName = nil;
+
+  cm = [GCSChannelManager defaultChannelManager];
+  folderLocation
+    = [[GCSFolderManager defaultFolderManager] folderInfoLocation];
+  fc = [cm acquireOpenChannelForURL: folderLocation];
+  if (fc)
+    {
+      sql
+       = [NSString stringWithFormat: (@"UPDATE %@ SET c_foldername = '%@'"
+                                      @" WHERE c_path = '%@'"),
+                   [folderLocation gcsTableName], newName, ocsPath];
+      [fc evaluateExpressionX: sql];
+      [cm releaseChannel: fc];
+//       sql = [sql stringByAppendingFormat:@" WHERE %@ = '%@'", 
+//                  uidColumnName, [self uid]];
+    }
+}
+
+- (NSArray *) fetchContentObjectNames
+{
   NSArray *fields, *records;
   
-  fields = [NSArray arrayWithObject:@"c_name"];
+  fields = [NSArray arrayWithObject: @"c_name"];
   records = [[self ocsFolder] fetchFields:fields matchingQualifier:nil];
-  if (![records isNotNull]) {
-    [self errorWithFormat:@"(%s): fetch failed!", __PRETTY_FUNCTION__];
-    return nil;
-  }
-  if ([records isKindOfClass:[NSException class]])
+  if (![records isNotNull])
+    {
+      [self errorWithFormat: @"(%s): fetch failed!", __PRETTY_FUNCTION__];
+      return nil;
+    }
+  if ([records isKindOfClass: [NSException class]])
     return records;
-  return [records valueForKey:@"c_name"];
+  return [records objectsForKey: @"c_name"];
+}
+
+- (BOOL) nameExistsInFolder: (NSString *) objectName
+{
+  NSArray *fields, *records;
+  EOQualifier *qualifier;
+
+  qualifier
+    = [EOQualifier qualifierWithQualifierFormat:
+                     [NSString stringWithFormat: @"c_name='%@'", objectName]];
+
+  fields = [NSArray arrayWithObject: @"c_name"];
+  records = [[self ocsFolder] fetchFields: fields
+                              matchingQualifier: qualifier];
+  return (records
+          && ![records isKindOfClass:[NSException class]]
+          && [records count] > 0);
 }
 
-- (NSDictionary *)fetchContentStringsAndNamesOfAllObjects {
+- (NSDictionary *) fetchContentStringsAndNamesOfAllObjects
+{
   NSDictionary *files;
   
   files = [[self ocsFolder] fetchContentsOfAllFiles];
-  if (![files isNotNull]) {
-    [self errorWithFormat:@"(%s): fetch failed!", __PRETTY_FUNCTION__];
-    return nil;
-  }
+  if (![files isNotNull])
+    {
+      [self errorWithFormat:@"(%s): fetch failed!", __PRETTY_FUNCTION__];
+      return nil;
+    }
   if ([files isKindOfClass:[NSException class]])
     return files;
   return files;
 
 /* reflection */
 
-- (NSString *)defaultFilenameExtension {
+- (NSString *) defaultFilenameExtension
+{
   /* 
      Override to add an extension to a filename
      
 
   if ([self respondsToSelector: @selector (groupDavResourceType)])
     {
-      groupDavCollection = [NSArray arrayWithObjects: [self groupDavResourceType],
-                                    @"http://groupdav.org/", @"G", nil];
-      rType = [NSArray arrayWithObjects: @"collection", groupDavCollection, nil];
+      groupDavCollection
+       = [NSArray arrayWithObjects: [self groupDavResourceType],
+                  XMLNS_GROUPDAV, nil];
+      rType = [NSArray arrayWithObjects: @"collection", groupDavCollection,
+                      nil];
     }
   else
     rType = [NSArray arrayWithObject: @"collection"];
   return rType;
 }
 
-- (NSArray *) toOneRelationshipKeys {
+- (NSString *) davContentType
+{
+  return @"httpd/unix-directory";
+}
+
+- (NSArray *) toOneRelationshipKeys
+{
   /* toOneRelationshipKeys are the 'files' contained in a folder */
   NSMutableArray *ma;
   NSArray  *names;
           name = [names objectAtIndex: i];
           r = [name rangeOfString: @"."];
           if (r.length == 0)
-            name = [[name stringByAppendingString:@"."] stringByAppendingString: ext];
-          [ma addObject:name];
+           name = [NSMutableString stringWithFormat: @"%@.%@", name, ext];
+          [ma addObject: name];
         }
 
       names = 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 pathArrayToSOGoObject]];
+}
+
+- (NSArray *) aclsForUser: (NSString *) uid
+{
+  NSMutableArray *acls;
+  NSArray *ownAcls, *containerAcls;
+
+  acls = [NSMutableArray array];
+  ownAcls = [self aclsForUser: uid
+                 forObjectAtPath: [self pathArrayToSOGoObject]];
+  [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 pathArrayToSOGoObject]];
+}
+
+- (void) removeAclsForUsers: (NSArray *) users
+{
+  return [self removeAclsForUsers: users
+               forObjectAtPath: [self pathArrayToSOGoObject]];
+}
+
+- (NSString *) defaultUserID
+{
+  return defaultUserID;
+}
+
+- (NSString *) httpURLForAdvisoryToUser: (NSString *) uid
+{
+  return [[self soURL] absoluteString];
+}
+
+- (NSString *) resourceURLForAdvisoryToUser: (NSString *) uid
+{
+  return [[self davURL] absoluteString];
+}
+
+- (id) lookupName: (NSString *) lookupName
+        inContext: (id) localContext
+          acquire: (BOOL) acquire
+{
+  id obj;
+  NSArray *davNamespaces;
+  NSDictionary *davInvocation;
+  NSString *objcMethod;
+
+  obj = [super lookupName: lookupName inContext: localContext
+              acquire: acquire];
+  if (!obj)
+    {
+      davNamespaces = [self davNamespaces];
+      if ([davNamespaces count] > 0)
+       {
+         davInvocation = [lookupName asDavInvocation];
+         if (davInvocation
+             && [davNamespaces
+                  containsObject: [davInvocation objectForKey: @"ns"]])
+           {
+             objcMethod = [[davInvocation objectForKey: @"method"]
+                            davMethodToObjC];
+             obj = [[SoSelectorInvocation alloc]
+                     initWithSelectorNamed:
+                       [NSString stringWithFormat: @"%@:", objcMethod]
+                     addContextParameter: YES];
+             [obj autorelease];
+           }
+       }
+    }
+
+  return obj;
+}
+
 /* WebDAV */
 
-- (BOOL)davIsCollection {
+- (NSArray *) davNamespaces
+{
+  return nil;
+}
+
+- (BOOL) davIsCollection
+{
   return [self isFolderish];
 }
 
 /* folder type */
 
-- (NSString *)outlookFolderClass {
+- (NSString *) outlookFolderClass
+{
+  [self subclassResponsibility: _cmd];
+
   return nil;
 }
 
 /* description */
 
-- (void)appendAttributesToDescription:(NSMutableString *)_ms {
+- (void) appendAttributesToDescription: (NSMutableString *) _ms
+{
   [super appendAttributesToDescription:_ms];
   
   [_ms appendFormat:@" ocs=%@", [self ocsPath]];
 }
 
-- (NSString *)loggingPrefix {
+- (NSString *) loggingPrefix
+{
   return [NSString stringWithFormat:@"<0x%08X[%@]:%@>",
                   self, NSStringFromClass([self class]),
                   [self nameInContainer]];