sogo/OpenChange/MAPIStoreDBFolder.m
Enrique J. Hernández Blasco dbfd86db04 oc: Set Editor as role is now possible in Outlook
According to [MS-OXCPERM] Section 2.2.7 in PidTagMemberRights possible
values, once we set the DeleteAny flag, the DeleteOwned flag must be set.
Likewise EditOwned must be set when EditAny is set. In this way,
the rights sent by the MAPI client are equal to the returned by the
server when Editor is set.

In real world practice, makes more strict Outlook 2013 work with editor permissions
the sharing of user's defined calendars, tasks or contacts folders as
the recipients can be editors of that folder.
2015-12-23 00:27:09 +01:00

395 lines
12 KiB
Objective-C

/* MAPIStoreDBFolder.m - this file is part of SOGo
*
* Copyright (C) 2011-2012 Inverse inc
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* 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.
*/
#include <inttypes.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
#import <Foundation/NSString.h>
#import <Foundation/NSURL.h>
#import <NGExtensions/NSObject+Logs.h>
#import <EOControl/EOQualifier.h>
#import <SOGo/SOGoFolder.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/EOQualifier+SOGoCacheObject.h>
#import "MAPIStoreContext.h"
#import "MAPIStoreDBFolderTable.h"
#import "MAPIStoreDBMessage.h"
#import "MAPIStoreDBMessageTable.h"
#import "MAPIStoreMapping.h"
#import "MAPIStoreTypes.h"
#import "MAPIStoreUserContext.h"
#import <SOGo/SOGoCacheGCSFolder.h>
#import "SOGoMAPIDBMessage.h"
#import "MAPIStoreDBFolder.h"
#undef DEBUG
#include <mapistore/mapistore.h>
#include <mapistore/mapistore_errors.h>
static Class EOKeyValueQualifierK, SOGoCacheGCSFolderK, MAPIStoreDBFolderK;
static NSString *MAPIStoreRightReadItems = @"RightsReadItems";
static NSString *MAPIStoreRightCreateItems = @"RightsCreateItems";
static NSString *MAPIStoreRightEditOwn = @"RightsEditOwn";
static NSString *MAPIStoreRightEditAll = @"RightsEditAll";
static NSString *MAPIStoreRightDeleteOwn = @"RightsDeleteOwn";
static NSString *MAPIStoreRightDeleteAll = @"RightsDeleteAll";
static NSString *MAPIStoreRightCreateSubfolders = @"RightsCreateSubfolders";
static NSString *MAPIStoreRightFolderOwner = @"RightsFolderOwner";
static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
@implementation MAPIStoreDBFolder
+ (void) initialize
{
EOKeyValueQualifierK = [EOKeyValueQualifier class];
SOGoCacheGCSFolderK = [SOGoCacheGCSFolder class];
MAPIStoreDBFolderK = [MAPIStoreDBFolder class];
}
- (void) setupAuxiliaryObjects
{
[super setupAuxiliaryObjects];
ASSIGN (sogoObject, dbFolder);
}
- (MAPIStoreMessageTable *) messageTable
{
return [MAPIStoreDBMessageTable tableForContainer: self];
}
- (MAPIStoreFolderTable *) folderTable
{
return [MAPIStoreDBFolderTable tableForContainer: self];
}
- (enum mapistore_error) createFolder: (struct SRow *) aRow
withFID: (uint64_t) newFID
andKey: (NSString **) newKeyP
{
enum mapistore_error rc;
NSString *folderName, *nameInContainer;
SOGoCacheGCSFolder *newFolder;
struct SPropValue *value;
value = get_SPropValue_SRow (aRow, PidTagDisplayName);
if (value)
folderName = [NSString stringWithUTF8String: value->value.lpszW];
else
{
value = get_SPropValue_SRow (aRow, PidTagDisplayName_string8);
if (value)
folderName = [NSString stringWithUTF8String: (const char *) value->value.lpszA];
else
folderName = nil;
}
if (folderName)
{
nameInContainer = [NSString stringWithFormat: @"0x%.16"PRIx64,
(unsigned long long) newFID];
newFolder = [SOGoCacheGCSFolderK objectWithName: nameInContainer
inContainer: sogoObject];
[newFolder reloadIfNeeded];
[[newFolder properties] setObject: folderName
forKey: MAPIPropertyKey (PidTagDisplayName)];
[newFolder save];
*newKeyP = nameInContainer;
rc = MAPISTORE_SUCCESS;
}
else
rc = MAPISTORE_ERR_INVALID_PARAMETER;
return rc;
}
- (enum mapistore_error) moveCopyToFolder: (MAPIStoreFolder *) targetFolder
withNewName: (NSString *) newFolderName
isMove: (BOOL) isMove
isRecursive: (BOOL) isRecursive
inMemCtx: (TALLOC_CTX *) memCtx
{
enum mapistore_error rc;
NSString *path, *pathComponent, *targetPath, *newPath;
NSString *newURL;
MAPIStoreMapping *mapping;
NSRange slashRange;
pathComponent = nil;
if (isMove && ([targetFolder isKindOfClass: MAPIStoreDBFolderK] || !targetFolder))
{
path = [sogoObject path];
slashRange = [path rangeOfString: @"/" options: NSBackwardsSearch];
if (slashRange.location == NSNotFound)
[NSException raise: @"MAPIStoreIOException"
format: @"db folder path must start with a '/'"];
else
pathComponent = [path substringFromIndex: slashRange.location + 1];
if (targetFolder)
{
targetPath = [[targetFolder sogoObject] path];
newPath = [NSString stringWithFormat: @"%@/%@",
targetPath, pathComponent];
[dbFolder changePathTo: newPath
intoNewContainer: [targetFolder dbFolder]];
}
else
[dbFolder changePathTo: [NSString stringWithFormat: @"/fallback/%@", pathComponent]
intoNewContainer: nil];
mapping = [self mapping];
if (targetFolder)
newURL = [NSString stringWithFormat: @"%@%@/",
[targetFolder url], pathComponent];
else
newURL = [NSString stringWithFormat: @"sogo://%@@fallback/%@/",
[[self userContext] username], pathComponent];
[mapping updateID: [self objectId]
withURL: newURL];
[targetFolder cleanupCaches];
rc = MAPISTORE_SUCCESS;
}
else
rc = [super moveCopyToFolder: targetFolder withNewName: newFolderName
isMove: isMove
isRecursive: isRecursive
inMemCtx: memCtx];
return rc;
}
- (MAPIStoreMessage *) createMessage
{
MAPIStoreMessage *newMessage;
SOGoMAPIDBMessage *fsObject;
NSString *newKey;
[[self userContext] activate];
newKey = [NSString stringWithFormat: @"%@.plist",
[SOGoObject globallyUniqueObjectId]];
fsObject = [SOGoMAPIDBMessage objectWithName: newKey
inContainer: sogoObject];
[fsObject setObjectType: MAPIMessageCacheObject];
[fsObject reloadIfNeeded];
newMessage = [MAPIStoreDBMessage mapiStoreObjectWithSOGoObject: fsObject
inContainer: self];
return newMessage;
}
- (NSArray *) messageKeysMatchingQualifier: (EOQualifier *) qualifier
andSortOrderings: (NSArray *) sortOrderings
{
NSArray *keys;
SOGoUser *ownerUser;
ownerUser = [[self userContext] sogoUser];
if ([[context activeUser] isEqual: ownerUser]
|| [self subscriberCanReadMessages])
{
[[self userContext] activate];
keys = [(SOGoCacheGCSFolder *) sogoObject childKeysOfType: MAPIMessageCacheObject
includeDeleted: NO
matchingQualifier: qualifier
andSortOrderings: sortOrderings];
}
else
keys = [NSArray array];
return keys;
}
- (NSArray *) folderKeysMatchingQualifier: (EOQualifier *) qualifier
andSortOrderings: (NSArray *) sortOrderings
{
[[self userContext] activate];
return [dbFolder childKeysOfType: MAPIFolderCacheObject
includeDeleted: NO
matchingQualifier: qualifier
andSortOrderings: sortOrderings];
}
/* TODO: now that we are DB-based, this method can easily be implemented
- (NSArray *) getDeletedKeysFromChangeNumber: (uint64_t) changeNum
andCN: (NSNumber **) cnNbrs
inTableType: (enum mapistore_table_type) tableType
{
}
*/
- (NSDate *) lastMessageModificationTime
{
NSUInteger count, max;
NSDate *date, *fileDate;
MAPIStoreDBMessage *msg;
NSArray *messageKeys;
messageKeys = [self messageKeys];
date = [NSCalendarDate date];
//[self logWithFormat: @"current date: %@", date];
max = [messageKeys count];
for (count = 0; count < max; count++)
{
msg = [self lookupMessage: [messageKeys objectAtIndex: count]];
fileDate = [msg lastModificationTime];
if ([date laterDate: fileDate] == fileDate)
{
//[self logWithFormat: @"current date: %@", date];
date = fileDate;
}
}
return date;
}
- (SOGoFolder *) aclFolder
{
return sogoObject;
}
- (NSArray *) rolesForExchangeRights: (uint32_t) rights
{
NSMutableArray *roles;
roles = [NSMutableArray arrayWithCapacity: 9];
if (rights & RightsReadItems)
[roles addObject: MAPIStoreRightReadItems];
if (rights & RightsCreateItems)
[roles addObject: MAPIStoreRightCreateItems];
if (rights & RightsEditOwn)
[roles addObject: MAPIStoreRightEditOwn];
if (rights & RightsDeleteOwn)
[roles addObject: MAPIStoreRightDeleteOwn];
if (rights & RightsEditAll)
[roles addObject: MAPIStoreRightEditAll];
if (rights & RightsDeleteAll)
[roles addObject: MAPIStoreRightDeleteAll];
if (rights & RightsCreateSubfolders)
[roles addObject: MAPIStoreRightCreateSubfolders];
if (rights & RightsFolderOwner)
[roles addObject: MAPIStoreRightFolderOwner];
if (rights & RightsFolderContact)
[roles addObject: MAPIStoreRightFolderContact];
return roles;
}
- (uint32_t) exchangeRightsForRoles: (NSArray *) roles
{
uint32_t rights = 0;
if ([roles containsObject: MAPIStoreRightReadItems])
rights |= RightsReadItems;
if ([roles containsObject: MAPIStoreRightCreateItems])
rights |= RightsCreateItems;
if ([roles containsObject: MAPIStoreRightEditOwn])
rights |= RightsEditOwn;
if ([roles containsObject: MAPIStoreRightDeleteOwn])
rights |= RightsDeleteOwn;
if ([roles containsObject: MAPIStoreRightEditAll])
rights |= RightsEditAll | RightsEditOwn;
if ([roles containsObject: MAPIStoreRightDeleteAll])
rights |= RightsDeleteAll | RightsDeleteOwn;
if ([roles containsObject: MAPIStoreRightCreateSubfolders])
rights |= RightsCreateSubfolders;
if ([roles containsObject: MAPIStoreRightFolderOwner])
rights |= RightsFolderOwner;
if ([roles containsObject: MAPIStoreRightFolderContact])
rights |= RightsFolderContact;
if (rights != 0)
rights |= RoleNone; /* actually "folder visible" */
return rights;
}
- (BOOL) _testRoleForActiveUser: (const NSString *) role
{
SOGoUser *activeUser;
NSArray *roles;
activeUser = [[self context] activeUser];
roles = [[self aclFolder] aclsForUser: [activeUser login]];
return [roles containsObject: role];
}
- (BOOL) subscriberCanCreateMessages
{
return [self _testRoleForActiveUser: MAPIStoreRightCreateItems];
}
- (BOOL) subscriberCanModifyMessages
{
return ([self _testRoleForActiveUser: MAPIStoreRightEditAll]
|| [self _testRoleForActiveUser: MAPIStoreRightEditOwn]);
}
- (BOOL) subscriberCanReadMessages
{
NSString *displayName;
/* when this folder is the "Freebusy Data" folder, we need to allow
subscribed to read an open contained messages in order to enable them to
find the "LocalFreebusy" message */
[sogoObject reloadIfNeeded];
displayName = [[sogoObject properties]
objectForKey: MAPIPropertyKey (PidTagDisplayName)];
return ([displayName isEqualToString: @"Freebusy Data"]
|| [self _testRoleForActiveUser: MAPIStoreRightReadItems]);
}
- (BOOL) subscriberCanDeleteMessages
{
return ([self _testRoleForActiveUser: MAPIStoreRightDeleteAll]
|| [self _testRoleForActiveUser: MAPIStoreRightDeleteOwn]);
}
- (BOOL) subscriberCanCreateSubFolders
{
return [self _testRoleForActiveUser: MAPIStoreRightCreateSubfolders];
}
- (BOOL) supportsSubFolders
{
return YES;
}
@end