]> err.no Git - sope/blobdiff - sope-gdl1/GDLContentStore/GCSFolderManager.m
fixed for OGo bug #1906
[sope] / sope-gdl1 / GDLContentStore / GCSFolderManager.m
index 68c9a9dcc587f812d199f2fbd54bcd871a8c1456..dfb64925ef673978648df5951659f81f4f944e05 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2004-2005 SKYRIX Software AG
+  Copyright (C) 2004-2007 SKYRIX Software AG
 
   This file is part of OpenGroupware.org.
 
@@ -57,6 +57,7 @@ static NSString   *GCSTypeRecordName        = @"c_folder_type";
 static NSString   *GCSPathRecordName        = @"c_path";
 static NSString   *GCSGenericFolderTypeName = @"Container";
 static const char *GCSPathColumnPattern     = "c_path%i";
+static NSCharacterSet *asciiAlphaNumericCS    = nil;
 
 + (void)initialize {
   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
@@ -64,6 +65,15 @@ static const char *GCSPathColumnPattern     = "c_path%i";
   debugOn     = [ud boolForKey:@"GCSFolderManagerDebugEnabled"];
   debugSQLGen = [ud boolForKey:@"GCSFolderManagerSQLDebugEnabled"];
   emptyArray  = [[NSArray alloc] init];
+  if (!asciiAlphaNumericCS)
+    {
+      asciiAlphaNumericCS
+       = [NSCharacterSet characterSetWithCharactersInString:
+                           @"0123456789"
+                         @"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                         @"abcdefghijklmnopqrstuvwxyz"];
+      [asciiAlphaNumericCS retain];
+    }
 }
 
 + (id)defaultFolderManager {
@@ -134,6 +144,7 @@ static const char *GCSPathColumnPattern     = "c_path%i";
   if ((self = [super init])) {
     self->channelManager = [[GCSChannelManager defaultChannelManager] retain];
     self->folderInfoLocation = [_url retain];
+    self->folderNamePrefix = nil;
 
     if ([[self folderInfoTableName] length] == 0) {
       [self logWithFormat:@"ERROR(%s): missing tablename in URL: %@", 
@@ -152,11 +163,22 @@ static const char *GCSPathColumnPattern     = "c_path%i";
   [self->nameToType         release];
   [self->folderInfoLocation release];
   [self->channelManager     release];
+  [self->folderNamePrefix   release];
   [super dealloc];
 }
 
 /* accessors */
 
+- (void) setFolderNamePrefix:(NSString *)_folderNamePrefix
+{
+  ASSIGN(self->folderNamePrefix, _folderNamePrefix);
+}
+
+- (NSString *) folderNamePrefix
+{
+  return self->folderNamePrefix;
+}
+
 - (NSURL *)folderInfoLocation {
   return self->folderInfoLocation;
 }
@@ -229,7 +251,7 @@ static const char *GCSPathColumnPattern     = "c_path%i";
   GCSFolderType *folderType;
   NSString      *folderTypeName, *locationString, *folderName, *path;
   NSNumber      *folderId;
-  NSURL         *location, *quickLocation;
+  NSURL         *location, *quickLocation, *aclLocation;
   
   if (_record == nil) return nil;
   
@@ -270,11 +292,17 @@ static const char *GCSPathColumnPattern     = "c_path%i";
     [self logWithFormat:@"WARNING(%s): missing quick location in record: %@", 
            __PRETTY_FUNCTION__, _record];
   }
+
+  locationString = [_record objectForKey:@"c_acl_location"];
+  aclLocation = [locationString isNotNull]
+    ? [NSURL URLWithString:locationString]
+    : nil;
   
   folder = [[GCSFolder alloc] initWithPath:path primaryKey:folderId
                              folderTypeName:folderTypeName 
                              folderType:folderType
                              location:location quickLocation:quickLocation
+                              aclLocation:aclLocation
                              folderManager:self];
   return [folder autorelease];
 }
@@ -561,8 +589,8 @@ static const char *GCSPathColumnPattern     = "c_path%i";
   [sql appendString:@"SELECT "];
   [sql appendString:@"c_folder_id, "];
   [sql appendString:@"c_path, "];
-  [sql appendString:@"c_location, c_quick_location, "];
-  [sql appendString:@"c_folder_type"];
+  [sql appendString:@"c_location, c_quick_location, c_acl_location,"];
+  [sql appendString:@" c_folder_type"];
   [sql appendString:@" FROM "];
   [sql appendString:[self folderInfoTableName]];
   [sql appendString:@" WHERE "];
@@ -599,21 +627,233 @@ static const char *GCSPathColumnPattern     = "c_path%i";
   return [self folderForRecord:record];
 }
 
+- (NSString *)sqlCreateWithTableName: (NSString *)_tabName {
+  return [NSString stringWithFormat: @"CREATE TABLE %@ (\n"
+                   @"  c_name VARCHAR (256) NOT NULL,\n"
+                   @"  c_content VARCHAR (100000) NOT NULL,\n"
+                   @"  c_creationdate INT4 NOT NULL,\n"
+                   @"  c_lastmodified INT4 NOT NULL,\n"
+                   @"  c_version INT4 NOT NULL\n"
+                   @")",
+                   _tabName];
+}
+
+- (NSString *)sqlAclCreateWithTableName: (NSString *)_tabName {
+  return [NSString stringWithFormat: @"CREATE TABLE %@ (\n"
+                   @"  c_uid VARCHAR (256) NOT NULL,\n"
+                   @"  c_object VARCHAR (256) NOT NULL,\n"
+                   @"  c_role VARCHAR (80) NOT NULL\n"
+                   @")",
+                   _tabName];
+}
+
+- (NSString *)baseTableNameForFolderAtPath:(NSString *)_path {
+  NSMutableString *fixedPath;
+  unsigned int count, max;
+  unichar currentChar;
+
+  fixedPath = [NSMutableString new];
+  [fixedPath autorelease];
+
+  if (self->folderNamePrefix != nil)
+    [fixedPath appendString: self->folderNamePrefix];
+
+  max = [_path length];
+  for (count = 0; count < max; count++) {
+    currentChar = [_path characterAtIndex: count];
+    if ([asciiAlphaNumericCS characterIsMember: currentChar])
+      [fixedPath appendFormat: @"%Lc", currentChar];
+    else
+      [fixedPath appendString: @"_"];
+  }
+
+  return (([fixedPath length] < 49)
+         ? (NSString *)fixedPath : [fixedPath substringToIndex: 49]);
+}
+
+- (NSString *)finalizedTableNameForBaseName:(NSString *)_baseName
+                                 atBaseURL:(NSString *)_baseURL
+                               withChannel:(EOAdaptorChannel *)_channel {
+  NSString *potentialName, *sqlTestFormat, *sqlTest;
+  unsigned int count;
+  
+  potentialName = _baseName;
+  sqlTestFormat = [NSString stringWithFormat: @"SELECT * FROM %@"
+                           @" WHERE c_location = '%@/%%@'"
+                           @" OR c_quick_location = '%@/%%@_quick'"
+                           @" OR c_acl_location = '%@/%%@_acl'",
+                           [self folderInfoTableName],
+                           _baseURL, _baseURL, _baseURL];
+
+  sqlTest = [NSString stringWithFormat: sqlTestFormat,
+                     potentialName, potentialName, potentialName];
+  count = 0;
+  while ([[self performSQL: sqlTest] isNotEmpty]) {
+    count++;
+    potentialName = [NSString stringWithFormat: @"%@%d", _baseName, count];
+    sqlTest = [NSString stringWithFormat: sqlTestFormat,
+                       potentialName, potentialName, potentialName];
+  }
+
+  return potentialName;
+}
+
 - (NSException *)createFolderOfType:(NSString *)_type atPath:(NSString *)_path{
-  // TODO: implement folder create
-  GCSFolderType *ftype;
-  
+  // TBD: badly broken, needs to be wrapped in a transaction.
+  // TBD: would be best to perform all operations as a single SQL statement.
+  GCSFolderType    *ftype;
+  NSString         *tableName, *quickTableName, *aclTableName;
+  NSString         *baseURL, *pathElement;
+  EOAdaptorChannel *channel;
+  NSEnumerator     *pathElements;
+  NSMutableArray   *paths;
+  NSException      *error;
+  NSString         *sql;
+
+  // TBD: fix SQL injection issue!
+  sql = [NSString stringWithFormat: @"SELECT * FROM %@ WHERE c_path = '%@'",
+                   [self folderInfoTableName], _path];
+  if ([[self performSQL: sql] isNotEmpty]) {
+    return [NSException exceptionWithName:@"GCSExitingFolder"
+                       reason:@"a folder already exists at that path"
+                       userInfo:nil];
+  }
   if ((ftype = [self folderTypeWithName:_type]) == nil) {
     return [NSException exceptionWithName:@"GCSMissingFolderType"
-                       reason:@"missing folder type"
+                       reason:@"missing folder type"userInfo:nil];
+  }
+  if ((channel = [self acquireOpenChannel]) == nil) {
+    return [NSException exceptionWithName:@"GCSNoChannel"
+                       reason:@"could not open channel"
                        userInfo:nil];
   }
   
-  [self logWithFormat:@"create folder of type: %@", ftype];
+  tableName = [self baseTableNameForFolderAtPath: _path];
+  baseURL
+    = [[folderInfoLocation absoluteString] stringByDeletingLastPathComponent];
+  tableName = [self finalizedTableNameForBaseName: tableName
+                   atBaseURL: baseURL withChannel: channel];
+  quickTableName = [tableName stringByAppendingString: @"_quick"];
+  aclTableName   = [tableName stringByAppendingString: @"_acl"];
+  
+  sql = [@"DROP TABLE " stringByAppendingString:quickTableName];
+  if ((error = [channel evaluateExpressionX:sql]) != nil)
+    ; // 'DROP TABLE' is allowed to fail (DROP IF EXISTS is not in PG<8.2)
+  
+  sql = [@"DROP TABLE " stringByAppendingString:tableName];
+  if ((error = [channel evaluateExpressionX:sql]) != nil)
+    ; // 'DROP TABLE' is allowed to fail (DROP IF EXISTS is not in PG<8.2)
+
+  sql = [@"DROP TABLE " stringByAppendingString:aclTableName];
+  if ((error = [channel evaluateExpressionX:sql]) != nil)
+    ; // 'DROP TABLE' is allowed to fail (DROP IF EXISTS is not in PG<8.2)
+  
+  sql = [self sqlCreateWithTableName: tableName];
+  if ((error = [channel evaluateExpressionX:sql]) != nil)
+    return error;
+  
+  sql = [ftype sqlQuickCreateWithTableName: quickTableName];
+  if ((error = [channel evaluateExpressionX:sql]) != nil) {
+    /* 'rollback' TBD: wrap in proper tx */
+    sql = [@"DROP TABLE " stringByAppendingString:tableName];
+    if ((error = [channel evaluateExpressionX:sql]) != nil) {
+      [self warnWithFormat:@"failed to drop freshly created table: %@", 
+             tableName];
+    }
+    
+    return error;
+  }
+  
+  sql = [self sqlAclCreateWithTableName: aclTableName];
+  if ((error = [channel evaluateExpressionX:sql]) != nil) {
+    /* 'rollback' TBD: wrap in proper tx */
+    sql = [@"DROP TABLE " stringByAppendingString:quickTableName];
+    if ((error = [channel evaluateExpressionX:sql]) != nil) {
+      [self warnWithFormat:@"failed to drop freshly created table: %@", 
+             tableName];
+    }
+    sql = [@"DROP TABLE " stringByAppendingString:tableName];
+    if ((error = [channel evaluateExpressionX:sql]) != nil) {
+      [self warnWithFormat:@"failed to drop freshly created table: %@", 
+             tableName];
+    }
+    
+    return error;
+  }
   
-  return [NSException exceptionWithName:@"NotYetImplemented"
-                     reason:@"no money, no time, ..."
-                     userInfo:nil];
+  paths = [[NSMutableArray alloc] initWithCapacity: 5];
+
+  pathElements = [[_path componentsSeparatedByString: @"/"] objectEnumerator];
+  while ((pathElement = [pathElements nextObject]) != nil) {
+    NSString *p = [[NSString alloc] initWithFormat: @"'%@'", pathElement];
+    [paths addObject: p];
+    [p release]; p = nil;
+  }
+  
+  while ([paths count] < 5)
+    [paths addObject: @"NULL"];
+  
+  // TBD: fix SQL injection issues
+  sql = [NSString stringWithFormat: @"INSERT INTO %@"
+                       @"        (c_path, c_path1, c_path2, c_path3, c_path4,"
+                       @"         c_foldername, c_location, c_quick_location,"
+                       @"         c_acl_location, c_folder_type)"
+                       @" VALUES ('%@', %@, %@, %@, %@, '%@', '%@/%@',"
+                       @"         '%@/%@', '%@/%@', '%@')",
+                       [self folderInfoTableName], _path,
+                       [paths objectAtIndex: 1], [paths objectAtIndex: 2],
+                       [paths objectAtIndex: 3], [paths objectAtIndex: 4],
+                       [_path lastPathComponent],
+                      baseURL, tableName,
+                      baseURL, quickTableName,
+                      baseURL, aclTableName,
+                       _type];
+  if ((error = [channel evaluateExpressionX:sql]) != nil)
+    return error;
+
+  [paths release]; paths = nil;
+  [self releaseChannel: channel];
+
+  return nil;
+}
+
+- (NSException *)deleteFolderAtPath:(NSString *)_path {
+  GCSFolder    *folder;
+  NSArray      *fnames;
+  NSString     *sql, *ws;
+  EOAdaptorChannel *channel;
+  NSException *ex;
+
+  if ((folder = [self folderAtPath:_path]) == nil) {
+    return [NSException exceptionWithName:@"GCSMissingFolder"
+                       reason:@"missing folder"
+                       userInfo:nil];
+  }
+
+  if ((fnames = [self internalNamesFromPath:_path]) == nil) {
+    [self debugWithFormat:@"got no internal names for path: '%@'", _path];
+    return nil;
+  }
+
+  ws = [self generateSQLWhereForInternalNames:fnames 
+            exactMatch:YES orDirectSubfolderMatch:NO];
+
+  sql = [NSString stringWithFormat: @"DELETE FROM %@ WHERE %@",
+                 [self folderInfoTableName], ws];
+  if ((channel = [self acquireOpenChannel]) == nil) {
+    return [NSException exceptionWithName:@"GCSNoChannel"
+                       reason:@"could not "
+                       userInfo:nil];
+  }
+
+  if ((ex = [channel evaluateExpressionX:sql]) != nil) {
+    [self releaseChannel:channel];
+    return ex;
+  }
+
+  [self releaseChannel:channel];
+
+  return [folder deleteFolder];
 }
 
 /* folder types */