2014-05-14 03:06:59 +02:00
|
|
|
/* SOGoCacheGCSFolder.m - this file is part of SOGo
|
2012-06-29 19:59:38 +02:00
|
|
|
*
|
2014-05-14 03:06:59 +02:00
|
|
|
* Copyright (C) 2012-2014 Inverse inc.
|
2012-06-29 19:59:38 +02:00
|
|
|
*
|
|
|
|
* This file is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 3, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* This file is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; see the file COPYING. If not, write to
|
|
|
|
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
|
|
* Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#import <Foundation/NSArray.h>
|
|
|
|
#import <Foundation/NSData.h>
|
|
|
|
#import <Foundation/NSDictionary.h>
|
|
|
|
#import <Foundation/NSException.h>
|
|
|
|
#import <Foundation/NSPropertyList.h>
|
|
|
|
#import <Foundation/NSString.h>
|
|
|
|
#import <Foundation/NSValue.h>
|
|
|
|
#import <Foundation/NSURL.h>
|
|
|
|
|
|
|
|
#import <NGExtensions/NSObject+Logs.h>
|
2012-08-13 19:48:43 +02:00
|
|
|
#import <NGExtensions/NSNull+misc.h>
|
2012-06-29 19:59:38 +02:00
|
|
|
#import <GDLAccess/EOAdaptorChannel.h>
|
|
|
|
#import <GDLContentStore/GCSChannelManager.h>
|
|
|
|
|
|
|
|
#import <SOGo/NSArray+Utilities.h>
|
2012-08-13 19:48:43 +02:00
|
|
|
#import <SOGo/NSString+Utilities.h>
|
2012-06-29 19:59:38 +02:00
|
|
|
#import <SOGo/SOGoDomainDefaults.h>
|
|
|
|
#import <SOGo/SOGoUser.h>
|
2014-05-14 03:06:59 +02:00
|
|
|
#import "EOQualifier+SOGoCacheObject.h"
|
|
|
|
#import "GCSSpecialQueries+SOGoCacheObject.h"
|
2012-06-29 19:59:38 +02:00
|
|
|
|
2014-05-14 03:06:59 +02:00
|
|
|
#import "SOGoCacheGCSFolder.h"
|
2012-06-29 19:59:38 +02:00
|
|
|
|
|
|
|
#undef DEBUG
|
2014-05-14 03:06:59 +02:00
|
|
|
//#include <stdbool.h>
|
|
|
|
//#include <talloc.h>
|
|
|
|
//#include <util/time.h>
|
|
|
|
//#include <mapistore/mapistore.h>
|
|
|
|
//#include <mapistore/mapistore_errors.h>
|
|
|
|
//#include <libmapiproxy.h>
|
|
|
|
//#include <param.h>
|
2012-06-29 19:59:38 +02:00
|
|
|
|
2014-05-14 03:06:59 +02:00
|
|
|
Class SOGoCacheGCSObjectK = Nil;
|
2012-06-29 19:59:38 +02:00
|
|
|
|
2014-05-14 03:06:59 +02:00
|
|
|
@implementation SOGoCacheGCSFolder
|
2012-06-29 19:59:38 +02:00
|
|
|
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
2014-05-14 03:06:59 +02:00
|
|
|
SOGoCacheGCSObjectK = [SOGoCacheGCSObject class];
|
2012-06-29 19:59:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]))
|
|
|
|
{
|
|
|
|
pathPrefix = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithName: (NSString *) name inContainer: (id) newContainer
|
|
|
|
{
|
|
|
|
if ((self = [super initWithName: name inContainer: newContainer]))
|
|
|
|
{
|
2014-05-13 23:54:02 +02:00
|
|
|
objectType = MAPIFolderCacheObject;
|
2014-05-14 03:06:59 +02:00
|
|
|
aclMessage = [SOGoCacheGCSObject objectWithName: @"permissions"
|
2012-06-29 19:59:38 +02:00
|
|
|
inContainer: self];
|
2014-05-13 23:54:02 +02:00
|
|
|
[aclMessage setObjectType: MAPIInternalCacheObject];
|
2012-06-29 19:59:38 +02:00
|
|
|
[aclMessage retain];
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
[aclMessage release];
|
|
|
|
[pathPrefix release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) isFolderish
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setPathPrefix: (NSString *) newPathPrefix
|
|
|
|
{
|
|
|
|
ASSIGN (pathPrefix, newPathPrefix);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSMutableString *) pathForChild: (NSString *) childName
|
|
|
|
{
|
|
|
|
NSMutableString *path;
|
|
|
|
|
|
|
|
path = [self path];
|
2012-08-13 16:35:00 +02:00
|
|
|
[path appendFormat: @"/%@", childName];
|
2012-06-29 19:59:38 +02:00
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSMutableString *) path
|
|
|
|
{
|
|
|
|
NSMutableString *path;
|
|
|
|
|
|
|
|
path = [super path];
|
|
|
|
if (pathPrefix)
|
|
|
|
[path insertString: pathPrefix atIndex: 0];
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - (SOGoMAPIDBMessage *) newMessage
|
|
|
|
// {
|
|
|
|
// NSString *newFilename;
|
|
|
|
|
|
|
|
// newFilename = [NSString stringWithFormat: @"%@.plist",
|
|
|
|
// [SOGoObject globallyUniqueObjectId]];
|
|
|
|
|
|
|
|
// return [SOGoMAPIDBMessage objectWithName: filename inContainer: self];
|
|
|
|
// }
|
|
|
|
|
2014-05-14 00:03:37 +02:00
|
|
|
- (NSArray *) childKeysOfType: (SOGoCacheObjectType) type
|
2012-06-29 19:59:38 +02:00
|
|
|
includeDeleted: (BOOL) includeDeleted
|
|
|
|
matchingQualifier: (EOQualifier *) qualifier
|
|
|
|
andSortOrderings: (NSArray *) sortOrderings
|
|
|
|
{
|
|
|
|
NSMutableArray *childKeys;
|
|
|
|
NSMutableString *sql// , *qualifierClause
|
|
|
|
;
|
|
|
|
NSString *childPathPrefix, *childPath, *childKey;
|
|
|
|
NSMutableArray *whereClause;
|
|
|
|
NSArray *records;
|
|
|
|
NSDictionary *record;
|
|
|
|
NSUInteger childPathPrefixLen, count, max;
|
2014-05-14 03:06:59 +02:00
|
|
|
SOGoCacheGCSObject *currentChild;
|
2012-06-29 19:59:38 +02:00
|
|
|
|
|
|
|
/* query construction */
|
|
|
|
sql = [NSMutableString stringWithCapacity: 256];
|
|
|
|
[sql appendFormat: @"SELECT * FROM %@", [self tableName]];
|
|
|
|
|
|
|
|
whereClause = [NSMutableArray arrayWithCapacity: 2];
|
2012-08-13 16:35:00 +02:00
|
|
|
[whereClause addObject: [NSString stringWithFormat: @"c_parent_path = '%@'",
|
|
|
|
[self path]]];
|
2012-06-29 19:59:38 +02:00
|
|
|
[whereClause addObject: [NSString stringWithFormat: @"c_type = %d", type]];
|
|
|
|
if (!includeDeleted)
|
|
|
|
[whereClause addObject: @"c_deleted = 0"];
|
|
|
|
|
|
|
|
[sql appendFormat: @" WHERE %@",
|
|
|
|
[whereClause componentsJoinedByString: @" AND "]];
|
|
|
|
|
2012-08-13 16:35:00 +02:00
|
|
|
childPathPrefix = [NSString stringWithFormat: @"%@/", [self path]];
|
|
|
|
|
2012-06-29 19:59:38 +02:00
|
|
|
/* results */
|
|
|
|
records = [self performSQLQuery: sql];
|
|
|
|
if (records)
|
|
|
|
{
|
|
|
|
max = [records count];
|
|
|
|
childKeys = [NSMutableArray arrayWithCapacity: max];
|
|
|
|
childPathPrefixLen = [childPathPrefix length];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
record = [records objectAtIndex: count];
|
|
|
|
childPath = [record objectForKey: @"c_path"];
|
|
|
|
childKey = [childPath substringFromIndex: childPathPrefixLen];
|
|
|
|
if ([childKey rangeOfString: @"/"].location == NSNotFound)
|
|
|
|
{
|
|
|
|
if (qualifier)
|
|
|
|
{
|
2014-05-14 03:06:59 +02:00
|
|
|
currentChild = [SOGoCacheGCSObject objectWithName: childKey
|
2012-06-29 19:59:38 +02:00
|
|
|
inContainer: self];
|
|
|
|
[currentChild setupFromRecord: record];
|
|
|
|
if ([qualifier evaluateSOGoMAPIDBObject: currentChild])
|
|
|
|
[childKeys addObject: childKey];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
[childKeys addObject: childKey];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
childKeys = nil;
|
|
|
|
|
|
|
|
return childKeys;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *) toManyRelationshipKeys
|
|
|
|
{
|
2014-05-13 23:54:02 +02:00
|
|
|
return [self childKeysOfType: MAPIFolderCacheObject
|
2012-06-29 19:59:38 +02:00
|
|
|
includeDeleted: NO
|
|
|
|
matchingQualifier: nil
|
|
|
|
andSortOrderings: nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *) toOneRelationshipKeys
|
|
|
|
{
|
2014-05-13 23:54:02 +02:00
|
|
|
return [self childKeysOfType: MAPIMessageCacheObject
|
2012-06-29 19:59:38 +02:00
|
|
|
includeDeleted: NO
|
|
|
|
matchingQualifier: nil
|
|
|
|
andSortOrderings: nil];
|
|
|
|
}
|
|
|
|
|
2012-08-15 21:00:38 +02:00
|
|
|
- (void) setNameInContainer: (NSString *) newName
|
|
|
|
{
|
|
|
|
NSMutableString *sql;
|
|
|
|
NSString *oldPath, *newPath, *path, *parentPath;
|
|
|
|
NSMutableArray *queries;
|
|
|
|
NSArray *records;
|
|
|
|
NSDictionary *record;
|
|
|
|
NSUInteger count, max;
|
|
|
|
|
|
|
|
/* change the paths in children records */
|
|
|
|
if (nameInContainer)
|
|
|
|
oldPath = [self path];
|
|
|
|
|
|
|
|
[super setNameInContainer: newName];
|
|
|
|
|
|
|
|
if (nameInContainer)
|
|
|
|
{
|
|
|
|
newPath = [self path];
|
|
|
|
|
|
|
|
sql = [NSMutableString stringWithFormat:
|
|
|
|
@"SELECT c_path, c_parent_path FROM %@"
|
|
|
|
@" WHERE c_path LIKE '%@/%%'",
|
|
|
|
[self tableName], oldPath];
|
|
|
|
records = [self performSQLQuery: sql];
|
|
|
|
max = [records count];
|
|
|
|
queries = [NSMutableArray arrayWithCapacity: max + 1];
|
|
|
|
if (max > 0)
|
|
|
|
{
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
record = [records objectAtIndex: count];
|
|
|
|
path = [record objectForKey: @"c_path"];
|
|
|
|
sql = [NSMutableString stringWithFormat: @"UPDATE %@"
|
|
|
|
@" SET c_path = '%@'",
|
|
|
|
[self tableName],
|
|
|
|
[path stringByReplacingPrefix: oldPath
|
|
|
|
withPrefix: newPath]];
|
|
|
|
parentPath = [record objectForKey: @"c_parent_path"];
|
|
|
|
if ([parentPath isNotNull])
|
|
|
|
[sql appendFormat: @", c_parent_path = '%@'",
|
|
|
|
[parentPath stringByReplacingPrefix: oldPath
|
|
|
|
withPrefix: newPath]];
|
|
|
|
[sql appendFormat: @" WHERE c_path = '%@'", path];
|
|
|
|
[queries addObject: sql];
|
|
|
|
}
|
|
|
|
[self performBatchSQLQueries: queries];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-13 19:48:43 +02:00
|
|
|
- (void) changePathTo: (NSString *) newPath
|
|
|
|
{
|
|
|
|
NSMutableString *sql// , *qualifierClause
|
|
|
|
;
|
|
|
|
NSString *oldPath, *oldPathAsPrefix, *path, *parentPath;
|
|
|
|
NSMutableArray *queries;
|
|
|
|
NSArray *records;
|
|
|
|
NSDictionary *record;
|
|
|
|
NSUInteger count, max;
|
|
|
|
|
|
|
|
/* change the paths in children records */
|
|
|
|
oldPath = [self path];
|
|
|
|
oldPathAsPrefix = [NSString stringWithFormat: @"%@/", oldPath];
|
|
|
|
|
|
|
|
sql = [NSMutableString stringWithFormat:
|
|
|
|
@"SELECT c_path, c_parent_path FROM %@"
|
|
|
|
@" WHERE c_path LIKE '%@%%'",
|
|
|
|
[self tableName], oldPathAsPrefix];
|
|
|
|
records = [self performSQLQuery: sql];
|
|
|
|
max = [records count];
|
|
|
|
queries = [NSMutableArray arrayWithCapacity: max + 1];
|
|
|
|
if (max > 0)
|
|
|
|
{
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
record = [records objectAtIndex: count];
|
|
|
|
path = [record objectForKey: @"c_path"];
|
2012-08-13 21:39:47 +02:00
|
|
|
sql = [NSMutableString stringWithFormat: @"UPDATE %@"
|
|
|
|
@" SET c_path = '%@'",
|
|
|
|
[self tableName],
|
|
|
|
[path stringByReplacingPrefix: oldPath
|
|
|
|
withPrefix: newPath]];
|
|
|
|
parentPath = [record objectForKey: @"c_parent_path"];
|
|
|
|
if ([parentPath isNotNull])
|
|
|
|
[sql appendFormat: @", c_parent_path = '%@'",
|
|
|
|
[parentPath stringByReplacingPrefix: oldPath
|
|
|
|
withPrefix: newPath]];
|
|
|
|
[sql appendFormat: @" WHERE c_path = '%@'", path];
|
|
|
|
[queries addObject: sql];
|
2012-08-13 19:48:43 +02:00
|
|
|
}
|
|
|
|
[self performBatchSQLQueries: queries];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* change the path in this folder record */
|
|
|
|
[super changePathTo: newPath];
|
|
|
|
}
|
|
|
|
|
2012-06-29 19:59:38 +02:00
|
|
|
// - (NSArray *) toOneRelationshipKeysMatchingQualifier: (EOQualifier *) qualifier
|
|
|
|
// andSortOrderings: (NSArray *) sortOrderings
|
|
|
|
// {
|
|
|
|
// NSArray *allKeys;
|
|
|
|
// NSMutableArray *keys;
|
|
|
|
// NSUInteger count, max;
|
|
|
|
// NSString *messageKey;
|
|
|
|
// SOGoMAPIDBMessage *message;
|
|
|
|
|
|
|
|
// if (sortOrderings)
|
|
|
|
// [self warnWithFormat: @"sorting is not handled yet"];
|
|
|
|
|
|
|
|
// allKeys = [self toOneRelationshipKeys];
|
|
|
|
// if (qualifier)
|
|
|
|
// {
|
|
|
|
// [self logWithFormat: @"%s: getting restricted FAI keys", __PRETTY_FUNCTION__];
|
|
|
|
// max = [allKeys count];
|
|
|
|
// keys = [NSMutableArray arrayWithCapacity: max];
|
|
|
|
// for (count = 0; count < max; count++)
|
|
|
|
// {
|
|
|
|
// messageKey = [allKeys objectAtIndex: count];
|
|
|
|
// message = [self lookupName: messageKey
|
|
|
|
// inContext: nil
|
|
|
|
// acquire: NO];
|
|
|
|
// if ([qualifier evaluateMAPIVolatileMessage: message])
|
|
|
|
// [keys addObject: messageKey];
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// else
|
|
|
|
// keys = (NSMutableArray *) allKeys;
|
|
|
|
|
|
|
|
// return keys;
|
|
|
|
// }
|
|
|
|
|
|
|
|
- (id) lookupName: (NSString *) childName
|
|
|
|
inContext: (WOContext *) woContext
|
|
|
|
acquire: (BOOL) acquire
|
|
|
|
{
|
|
|
|
id object;
|
|
|
|
Class objectClass;
|
|
|
|
NSString *childPath;
|
|
|
|
NSDictionary *record;
|
|
|
|
|
|
|
|
childPath = [self pathForChild: childName];
|
|
|
|
record = [self lookupRecord: childPath newerThanVersion: -1];
|
|
|
|
if (record)
|
|
|
|
{
|
2014-05-13 23:54:02 +02:00
|
|
|
if ([[record objectForKey: @"c_type"] intValue] == MAPIFolderCacheObject)
|
2012-06-29 19:59:38 +02:00
|
|
|
objectClass = isa;
|
|
|
|
else
|
2014-05-14 03:06:59 +02:00
|
|
|
objectClass = SOGoCacheGCSObjectK;
|
2012-06-29 19:59:38 +02:00
|
|
|
|
|
|
|
object = [objectClass objectWithName: childName
|
|
|
|
inContainer: self];
|
|
|
|
[object setupFromRecord: record];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
object = nil;
|
|
|
|
|
|
|
|
return object;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) lookupFolder: (NSString *) folderName
|
|
|
|
inContext: (WOContext *) woContext
|
|
|
|
{
|
|
|
|
id object;
|
|
|
|
|
2014-05-14 03:06:59 +02:00
|
|
|
object = [SOGoCacheGCSFolder objectWithName: folderName
|
2012-06-29 19:59:38 +02:00
|
|
|
inContainer: self];
|
|
|
|
[object reloadIfNeeded];
|
|
|
|
|
|
|
|
return object;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - (id) _fileAttributeForKey: (NSString *) key
|
|
|
|
// {
|
|
|
|
// NSDictionary *attributes;
|
|
|
|
|
|
|
|
// attributes = [[NSFileManager defaultManager]
|
|
|
|
// fileAttributesAtPath: directory
|
|
|
|
// traverseLink: NO];
|
|
|
|
|
|
|
|
// return [attributes objectForKey: key];
|
|
|
|
// }
|
|
|
|
|
|
|
|
// - (NSCalendarDate *) creationTime
|
|
|
|
// {
|
|
|
|
// return [self _fileAttributeForKey: NSFileCreationDate];
|
|
|
|
// }
|
|
|
|
|
|
|
|
// - (NSCalendarDate *) lastModificationTime
|
|
|
|
// {
|
|
|
|
// return [self _fileAttributeForKey: NSFileModificationDate];
|
|
|
|
// }
|
|
|
|
|
|
|
|
/* acl */
|
|
|
|
- (NSString *) defaultUserID
|
|
|
|
{
|
|
|
|
return @"default";
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSMutableDictionary *) _aclEntries
|
|
|
|
{
|
|
|
|
NSMutableDictionary *aclEntries;
|
|
|
|
|
|
|
|
[aclMessage reloadIfNeeded];
|
|
|
|
aclEntries = [aclMessage properties];
|
|
|
|
if (![aclEntries objectForKey: @"users"])
|
|
|
|
[aclEntries setObject: [NSMutableArray array] forKey: @"users"];
|
|
|
|
if (![aclEntries objectForKey: @"entries"])
|
|
|
|
[aclEntries setObject: [NSMutableDictionary dictionary]
|
|
|
|
forKey: @"entries"];
|
|
|
|
|
|
|
|
return aclEntries;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) addUserInAcls: (NSString *) user
|
|
|
|
{
|
|
|
|
NSMutableDictionary *acl;
|
|
|
|
NSMutableArray *users;
|
|
|
|
|
|
|
|
acl = [self _aclEntries];
|
|
|
|
users = [acl objectForKey: @"users"];
|
|
|
|
[users addObjectUniquely: user];
|
|
|
|
[aclMessage save];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) removeAclsForUsers: (NSArray *) oldUsers
|
|
|
|
{
|
|
|
|
NSDictionary *acl;
|
|
|
|
NSMutableDictionary *entries;
|
|
|
|
NSMutableArray *users;
|
|
|
|
|
|
|
|
acl = [self _aclEntries];
|
|
|
|
entries = [acl objectForKey: @"entries"];
|
|
|
|
[entries removeObjectsForKeys: oldUsers];
|
|
|
|
users = [acl objectForKey: @"users"];
|
|
|
|
[users removeObjectsInArray: oldUsers];
|
|
|
|
[aclMessage save];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *) aclUsers
|
|
|
|
{
|
|
|
|
return [[self _aclEntries] objectForKey: @"users"];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *) aclsForUser: (NSString *) uid
|
|
|
|
{
|
|
|
|
NSDictionary *entries;
|
|
|
|
|
|
|
|
entries = [[self _aclEntries] objectForKey: @"entries"];
|
|
|
|
|
|
|
|
return [entries objectForKey: uid];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setRoles: (NSArray *) roles
|
|
|
|
forUser: (NSString *) uid
|
|
|
|
{
|
|
|
|
NSMutableDictionary *acl;
|
|
|
|
NSMutableDictionary *entries;
|
|
|
|
|
|
|
|
acl = [self _aclEntries];
|
|
|
|
entries = [acl objectForKey: @"entries"];
|
|
|
|
[entries setObject: roles forKey: uid];
|
|
|
|
[aclMessage save];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|