diff --git a/OpenChange/GNUmakefile b/OpenChange/GNUmakefile index 4fc207904..739fbd95b 100644 --- a/OpenChange/GNUmakefile +++ b/OpenChange/GNUmakefile @@ -78,6 +78,9 @@ $(SOGOBACKEND)_OBJC_FILES += \ MAPIStoreGCSMessage.m \ MAPIStoreGCSMessageTable.m \ \ + MAPIStoreCalTaskFolder.m \ + MAPIStoreCalTaskMessage.m \ + \ MAPIStoreCalendarAttachment.m \ MAPIStoreCalendarContext.m \ MAPIStoreCalendarFolder.m \ diff --git a/OpenChange/MAPIStoreAppointmentWrapper.h b/OpenChange/MAPIStoreAppointmentWrapper.h index 998f3e175..5ed4c83ff 100644 --- a/OpenChange/MAPIStoreAppointmentWrapper.h +++ b/OpenChange/MAPIStoreAppointmentWrapper.h @@ -68,6 +68,10 @@ - (void) fillMessageData: (struct mapistore_message *) dataPtr inMemCtx: (TALLOC_CTX *) memCtx; +- (NSString *) creator; +- (NSString *) owner; +- (NSUInteger) sensitivity; + - (enum mapistore_error) getPidTagSenderEmailAddress: (void **) data inMemCtx: (TALLOC_CTX *) memCtx; - (enum mapistore_error) getPidTagSenderAddressType: (void **) data diff --git a/OpenChange/MAPIStoreAppointmentWrapper.m b/OpenChange/MAPIStoreAppointmentWrapper.m index 4b1d901ec..1881a16a5 100644 --- a/OpenChange/MAPIStoreAppointmentWrapper.m +++ b/OpenChange/MAPIStoreAppointmentWrapper.m @@ -1023,6 +1023,86 @@ static NSCharacterSet *hexCharacterSet = nil; inMemCtx: memCtx]; } +/* creator (only if created from Outlook/SOGo or organizer as fallback */ +- (NSString *) creator +{ + iCalPerson *person; + NSDictionary *contactInfos; + NSString *creator = nil, *email; + SOGoUserManager *mgr; + + creator = [[event uniqueChildWithTag: @"x-sogo-component-created-by"] + flattenedValuesForKey: @""]; + if ([creator length] == 0) + { + person = [event organizer]; + if (person) + { + email = [person rfc822Email]; + if ([email length] > 0) + { + mgr = [SOGoUserManager sharedUserManager]; + contactInfos = [mgr contactInfosForUserWithUIDorEmail: email]; + if (contactInfos) + creator = [contactInfos objectForKey: @"sAMAccountName"]; + } + } + } + return creator; +} + +/* owner is the organizer of the event, if none, try with the creator + who has saved only from Outlook or SOGo */ +- (NSString *) owner +{ + iCalPerson *person; + NSDictionary *contactInfos; + NSString *email, *owner = nil; + SOGoUserManager *mgr; + + person = [event organizer]; + if (person) + { + email = [person rfc822Email]; + if ([email length] > 0) + { + mgr = [SOGoUserManager sharedUserManager]; + contactInfos = [mgr contactInfosForUserWithUIDorEmail: email]; + if (contactInfos) + owner = [contactInfos objectForKey: @"sAMAccountName"]; + } + } + + if (!owner) + owner = [[event uniqueChildWithTag: @"x-sogo-component-created-by"] + flattenedValuesForKey: @""]; + + return owner; +} + +- (NSUInteger) sensitivity +{ + NSString *accessClass = nil; + NSUInteger v; + + accessClass = [event accessClass]; + if (accessClass) + { + if ([accessClass isEqualToString: @"X-PERSONAL"]) + v = 0x1; + else if ([accessClass isEqualToString: @"PRIVATE"]) + v = 0x2; + else if ([accessClass isEqualToString: @"CONFIDENTIAL"]) + v = 0x3; + else + v = 0x0; /* PUBLIC */ + } + else + v = 0x0; /* PUBLIC */ + + return v; +} + /* sender representing */ - (enum mapistore_error) getPidTagSentRepresentingEmailAddress: (void **) data inMemCtx: (TALLOC_CTX *) memCtx @@ -1185,23 +1265,8 @@ static NSCharacterSet *hexCharacterSet = nil; { /* See [MS-OXCICAL] Section 2.1.3.11.20.4 */ uint32_t v; - NSString *accessClass; - - accessClass = [event accessClass]; - if (accessClass) - { - if ([accessClass isEqualToString: @"X-PERSONAL"]) - v = 0x1; - else if ([accessClass isEqualToString: @"PRIVATE"]) - v = 0x2; - else if ([accessClass isEqualToString: @"CONFIDENTIAL"]) - v = 0x3; - else - v = 0x0; /* PUBLIC */ - } - else - v = 0x0; /* PUBLIC */ + v = (uint32_t) [self sensitivity]; *data = MAPILongValue (memCtx, v); return MAPISTORE_SUCCESS; diff --git a/OpenChange/MAPIStoreCalTaskFolder.h b/OpenChange/MAPIStoreCalTaskFolder.h new file mode 100644 index 000000000..92b75e463 --- /dev/null +++ b/OpenChange/MAPIStoreCalTaskFolder.h @@ -0,0 +1,35 @@ +/* MAPIStoreCalTaskFolder.h - this file is part of SOGo + * + * Copyright (C) 2016 Enrique J. Hernandez + * + * Author: Enrique J. Hernandez + * + * 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 2, 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. + */ + +#ifndef MAPISTORECALTASKFOLDER_H +#define MAPISTORECALTASKFOLDER_H + +#import "MAPIStoreGCSFolder.h" + +/* This class is intended to share code between Calendar and Tasks as + of nowadays share the table */ + +@interface MAPIStoreCalTaskFolder : MAPIStoreGCSFolder + +@end + +#endif /* MAPISTORECALTASKFOLDER_H */ diff --git a/OpenChange/MAPIStoreCalTaskFolder.m b/OpenChange/MAPIStoreCalTaskFolder.m new file mode 100644 index 000000000..8876c77e5 --- /dev/null +++ b/OpenChange/MAPIStoreCalTaskFolder.m @@ -0,0 +1,101 @@ +/* MAPIStoreCalTaskFolder.m - this file is part of SOGo + * + * Copyright (C) 2016 Enrique J. Hernandez + * + * Author: Enrique J. Hernandez + * + * 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 +#import +#import +#import + +#import + +#import "MAPIStoreCalTaskFolder.h" + +@implementation MAPIStoreCalTaskFolder + +- (NSArray *) expandRoles: (NSArray *) roles +{ + static NSDictionary *rolesMap = nil; + NSArray *subRoles; + NSMutableSet *expandedRoles; + NSString *role, *subRole; + NSUInteger i, max, j; + + if (!rolesMap) + { + /* Build the map of array permissions */ + rolesMap = [[NSDictionary alloc] initWithObjects: [NSArray arrayWithObjects: + [NSArray arrayWithObjects: SOGoCalendarRole_PublicDAndTViewer, + SOGoCalendarRole_PublicViewer, + SOGoCalendarRole_PublicResponder, + SOGoCalendarRole_PublicModifier, nil], + [NSArray arrayWithObjects: SOGoCalendarRole_ConfidentialDAndTViewer, + SOGoCalendarRole_ConfidentialViewer, + SOGoCalendarRole_ConfidentialResponder, + SOGoCalendarRole_ConfidentialModifier, nil], + [NSArray arrayWithObjects: SOGoCalendarRole_PrivateDAndTViewer, + SOGoCalendarRole_PrivateViewer, + SOGoCalendarRole_PrivateResponder, + SOGoCalendarRole_PrivateModifier, nil], + [NSArray arrayWithObjects: SOGoCalendarRole_ComponentDAndTViewer, + SOGoCalendarRole_ComponentViewer, + SOGoCalendarRole_ComponentResponder, + SOGoCalendarRole_ComponentModifier, nil], + nil] + forKeys: [NSArray arrayWithObjects: @"Public", @"Confidential", @"Private", + @"Component", nil]]; + } + + max = [roles count]; + expandedRoles = [NSMutableSet set]; + for (i = 0; i < max; i++) + { + role = [roles objectAtIndex: i]; + subRoles = nil; + if ([role hasPrefix: @"Public"]) + subRoles = [rolesMap objectForKey: @"Public"]; + else if ([role hasPrefix: @"Confidential"]) + subRoles = [rolesMap objectForKey: @"Confidential"]; + else if ([role hasPrefix: @"Private"]) + subRoles = [rolesMap objectForKey: @"Private"]; + else if ([role hasPrefix: @"Component"]) + subRoles = [rolesMap objectForKey: @"Component"]; + + if (subRoles) + { + for (j = 0; j < [subRoles count]; j++) + { + subRole = [subRoles objectAtIndex: j]; + [expandedRoles addObject: subRole]; + + if ([subRole isEqualToString: role]) + break; + } + } + else + { + [expandedRoles addObject: role]; + } + } + + return [expandedRoles allObjects]; +} + +@end diff --git a/OpenChange/MAPIStoreCalTaskMessage.h b/OpenChange/MAPIStoreCalTaskMessage.h new file mode 100644 index 000000000..8e284b414 --- /dev/null +++ b/OpenChange/MAPIStoreCalTaskMessage.h @@ -0,0 +1,40 @@ +/* MAPIStoreCalTaskMessage.h - this file is part of SOGo + * + * Copyright (C) 2016 Enrique J. Hernandez + * + * Author: Enrique J. Hernandez + * + * 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 2, 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. + */ + +#ifndef MAPISTORECALTASKMESSAGE_H +#define MAPISTORECALTASKMESSAGE_H + +#import "MAPIStoreGCSMessage.h" + +/* This class is intended to share common logic for Calendar and Tasks + as of today they are stored in the same table. This is relevant for + permissions */ +@interface MAPIStoreCalTaskMessage : MAPIStoreGCSMessage +{ +} + +/* Get the sensitivity (access class) from a message */ +- (NSUInteger) sensitivity; + +@end + +#endif /* MAPISTORECALTASKMESSAGE_H */ diff --git a/OpenChange/MAPIStoreCalTaskMessage.m b/OpenChange/MAPIStoreCalTaskMessage.m new file mode 100644 index 000000000..b88e9c2ef --- /dev/null +++ b/OpenChange/MAPIStoreCalTaskMessage.m @@ -0,0 +1,107 @@ +/* MAPIStoreCalTaskMessage.h - this file is part of SOGo + * + * Copyright (C) 2016 Enrique J. Hernandez + * + * Author: Enrique J. Hernandez + * + * 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 2, 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 +#import + +#import + +#import + +#import "MAPIStoreFolder.h" +#import "MAPIStoreCalTaskMessage.h" + +@implementation MAPIStoreCalTaskMessage + +/* It must be implemented by subclasses */ +- (NSUInteger) sensitivity +{ + [self subclassResponsibility: _cmd]; + + return 0; +} + +- (NSArray *) expandRoles: (NSArray *) roles +{ + return [container expandRoles: roles]; +} + +- (BOOL) subscriberCanModifyMessage +{ + BOOL rc; + NSArray *roles; + + roles = [self activeUserRoles]; + + if (isNew) + rc = [roles containsObject: SOGoRole_ObjectCreator]; + else + rc = ([roles containsObject: SOGoCalendarRole_ComponentModifier] + || [roles containsObject: SOGoCalendarRole_ComponentResponder]); + + /* Check if the message is owned and it has permission to edit it */ + if (!rc && [roles containsObject: MAPIStoreRightEditOwn]) + { + NSString *currentUser; + + currentUser = [[container context] activeUser]; + rc = [currentUser isEqual: [self ownerUser]]; + } + + if (!rc) + { + NSUInteger sensitivity; + + /* Get sensitivity of the message to check if the user can + modify the message */ + sensitivity = [self sensitivity]; + /* FIXME: Use OpenChange constant names */ + switch (sensitivity) + { + case 0: /* PUBLIC */ + rc = [roles containsObject: SOGoCalendarRole_PublicModifier] + || [roles containsObject: SOGoCalendarRole_PublicResponder]; + break; + case 1: /* PERSONAL */ + case 2: /* PRIVATE */ + rc = [roles containsObject: SOGoCalendarRole_PrivateModifier] + || [roles containsObject: SOGoCalendarRole_PrivateResponder]; + break; + case 3: /* CONFIDENTIAL */ + rc = [roles containsObject: SOGoCalendarRole_ConfidentialModifier] + || [roles containsObject: SOGoCalendarRole_ConfidentialResponder]; + } + } + return rc; +} + +- (BOOL) subscriberCanReadMessage +{ + NSArray *roles; + + roles = [self activeUserRoles]; + + return ([roles containsObject: SOGoCalendarRole_ComponentViewer] + || [roles containsObject: SOGoCalendarRole_ComponentDAndTViewer] + || [self subscriberCanModifyMessage]); +} + +@end diff --git a/OpenChange/MAPIStoreCalendarEmbeddedMessage.m b/OpenChange/MAPIStoreCalendarEmbeddedMessage.m index 6f0c8a776..02a776949 100644 --- a/OpenChange/MAPIStoreCalendarEmbeddedMessage.m +++ b/OpenChange/MAPIStoreCalendarEmbeddedMessage.m @@ -208,6 +208,7 @@ [[container event] updateFromMAPIProperties: properties inUserContext: [self userContext] withActiveUser: activeUser + isNew: NO inMemCtx: memCtx]; } diff --git a/OpenChange/MAPIStoreCalendarFolder.h b/OpenChange/MAPIStoreCalendarFolder.h index fd6c5d20f..9b3a4cf5c 100644 --- a/OpenChange/MAPIStoreCalendarFolder.h +++ b/OpenChange/MAPIStoreCalendarFolder.h @@ -23,9 +23,9 @@ #ifndef MAPISTORECALENDARFOLDER_H #define MAPISTORECALENDARFOLDER_H -#import "MAPIStoreGCSFolder.h" +#import "MAPIStoreCalTaskFolder.h" -@interface MAPIStoreCalendarFolder : MAPIStoreGCSFolder +@interface MAPIStoreCalendarFolder : MAPIStoreCalTaskFolder @end diff --git a/OpenChange/MAPIStoreCalendarFolder.m b/OpenChange/MAPIStoreCalendarFolder.m index 30547cb02..ed949e638 100644 --- a/OpenChange/MAPIStoreCalendarFolder.m +++ b/OpenChange/MAPIStoreCalendarFolder.m @@ -81,11 +81,7 @@ Following rights are not supported by SOGo specifically: - - DeleteOwned : Delete only own objects - - EditOwned : Edit only own objects - CreateSubfolders: No calendar subfolders - - FolderOwner: No sharing folder ownership? - - FolderContact: No support to store this information - FolderVisible: It is inferred by other rights when extracting */ NSMutableArray *roles; @@ -95,26 +91,35 @@ [roles addObject: SOGoRole_ObjectCreator]; if (rights & RightsDeleteAll) [roles addObject: SOGoRole_ObjectEraser]; + if (rights & RightsDeleteOwn) + [roles addObject: MAPIStoreRightDeleteOwn]; + if (rights & RightsEditAll) { [roles addObject: SOGoCalendarRole_PublicModifier]; [roles addObject: SOGoCalendarRole_PrivateModifier]; [roles addObject: SOGoCalendarRole_ConfidentialModifier]; } - else if (rights & RightsReadItems) + if (rights & RightsEditOwn) + [roles addObject: MAPIStoreRightEditOwn]; + + if (rights & RightsReadItems) { [roles addObject: SOGoCalendarRole_PublicViewer]; [roles addObject: SOGoCalendarRole_PrivateViewer]; [roles addObject: SOGoCalendarRole_ConfidentialViewer]; } if (rights & RightsFreeBusySimple) - { - [roles addObject: SOGoCalendarRole_PublicDAndTViewer]; - } + [roles addObject: SOGoCalendarRole_PublicDAndTViewer]; + if (rights & RightsFreeBusyDetailed) - { - [roles addObject: SOGoCalendarRole_ConfidentialDAndTViewer]; - } + [roles addObject: SOGoCalendarRole_ConfidentialDAndTViewer]; + + if (rights & RightsFolderOwner) + [roles addObject: MAPIStoreRightFolderOwner]; + + if (rights & RightsFolderContact) + [roles addObject: MAPIStoreRightFolderContact]; // [self logWithFormat: @"roles for rights %.8x = (%@)", rights, roles]; @@ -136,19 +141,28 @@ else if ([roles containsObject: SOGoCalendarRole_PublicViewer] && [roles containsObject: SOGoCalendarRole_PrivateViewer] && [roles containsObject: SOGoCalendarRole_ConfidentialViewer]) - // We have to set by hand other rights as only the highest role is returned - // See SOGoAppointmentFolder.m:aclsForUser for details - rights |= RightsReadItems | RightsFreeBusySimple | RightsFreeBusyDetailed; + rights |= RightsReadItems; + + if ([roles containsObject: MAPIStoreRightEditOwn]) + rights |= RightsEditOwn; + if ([roles containsObject: MAPIStoreRightDeleteOwn]) + rights |= RightsDeleteOwn; if ([roles containsObject: SOGoCalendarRole_PublicDAndTViewer]) rights |= RightsFreeBusySimple; if ([roles containsObject: SOGoCalendarRole_ConfidentialDAndTViewer]) - rights |= RightsFreeBusyDetailed; + rights |= RightsFreeBusySimple | RightsFreeBusyDetailed; if ((rights & RightsReadItems) != 0 || (rights & RightsCreateItems) != 0 || (rights & RightsDeleteAll) != 0) rights |= RoleNone; /* actually "folder visible" */ + if ([roles containsObject: MAPIStoreRightFolderOwner]) + rights |= RightsFolderOwner | RoleNone; + + if ([roles containsObject: MAPIStoreRightFolderContact]) + rights |= RightsFolderContact; + // [self logWithFormat: @"rights for roles (%@) = %.8x", roles, rights]; return rights; diff --git a/OpenChange/MAPIStoreCalendarMessage.h b/OpenChange/MAPIStoreCalendarMessage.h index 8b2de759d..99e286073 100644 --- a/OpenChange/MAPIStoreCalendarMessage.h +++ b/OpenChange/MAPIStoreCalendarMessage.h @@ -23,13 +23,13 @@ #ifndef MAPISTORECALENDARMESSAGE_H #define MAPISTORECALENDARMESSAGE_H -#import "MAPIStoreGCSMessage.h" +#import "MAPIStoreCalTaskMessage.h" @class iCalCalendar; @class iCalEvent; @class MAPIStoreAppointmentWrapper; -@interface MAPIStoreCalendarMessage : MAPIStoreGCSMessage +@interface MAPIStoreCalendarMessage : MAPIStoreCalTaskMessage { iCalCalendar *calendar; iCalEvent *masterEvent; diff --git a/OpenChange/MAPIStoreCalendarMessage.m b/OpenChange/MAPIStoreCalendarMessage.m index 62a29f118..5ec92d07f 100644 --- a/OpenChange/MAPIStoreCalendarMessage.m +++ b/OpenChange/MAPIStoreCalendarMessage.m @@ -528,29 +528,19 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK; ASSIGN (sogoObject, newObject); } -- (BOOL) subscriberCanReadMessage +- (NSUInteger) sensitivity { - NSArray *roles; - - roles = [self activeUserRoles]; - - return ([roles containsObject: SOGoCalendarRole_ComponentViewer] - || [roles containsObject: SOGoCalendarRole_ComponentDAndTViewer] - || [self subscriberCanModifyMessage]); + return [[self _appointmentWrapper] sensitivity]; } -- (BOOL) subscriberCanModifyMessage +- (NSString *) creator { - BOOL rc; - NSArray *roles = [self activeUserRoles]; + return [[self _appointmentWrapper] creator]; +} - if (isNew) - rc = [roles containsObject: SOGoRole_ObjectCreator]; - else - rc = ([roles containsObject: SOGoCalendarRole_ComponentModifier] - || [roles containsObject: SOGoCalendarRole_ComponentResponder]); - - return rc; +- (NSString *) owner +{ + return [[self _appointmentWrapper] owner]; } - (void) _updateAttachedEvents @@ -632,6 +622,7 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK; [masterEvent updateFromMAPIProperties: properties inUserContext: [self userContext] withActiveUser: activeUser + isNew: isNew inMemCtx: memCtx]; [self _updateAttachedEvents]; [[self userContext] activate]; diff --git a/OpenChange/MAPIStoreContactsFolder.m b/OpenChange/MAPIStoreContactsFolder.m index fd6db947a..d55c34a25 100644 --- a/OpenChange/MAPIStoreContactsFolder.m +++ b/OpenChange/MAPIStoreContactsFolder.m @@ -74,18 +74,34 @@ - (NSArray *) rolesForExchangeRights: (uint32_t) rights { + /* Limitations + + Following rights are not supported by SOGo specifically: + + - CreateSubfolders: No contacts subfolders + - FolderVisible: It is inferred by other rights when extracting + */ NSMutableArray *roles; - roles = [NSMutableArray arrayWithCapacity: 6]; + roles = [NSMutableArray arrayWithCapacity: 8]; if (rights & RightsCreateItems) [roles addObject: SOGoRole_ObjectCreator]; if (rights & RightsDeleteAll) [roles addObject: SOGoRole_ObjectEraser]; + if (rights & RightsDeleteOwn) + [roles addObject: MAPIStoreRightDeleteOwn]; if (rights & RightsEditAll) [roles addObject: SOGoRole_ObjectEditor]; + if (rights & RightsEditOwn) + [roles addObject: MAPIStoreRightEditOwn]; if (rights & RightsReadItems) [roles addObject: SOGoRole_ObjectViewer]; + if (rights & RightsFolderOwner) + [roles addObject: MAPIStoreRightFolderOwner]; + if (rights & RightsFolderContact) + [roles addObject: MAPIStoreRightFolderContact]; + return roles; } @@ -95,15 +111,28 @@ if ([roles containsObject: SOGoRole_ObjectCreator]) rights |= RightsCreateItems; + if ([roles containsObject: SOGoRole_ObjectEraser]) rights |= RightsDeleteAll | RightsDeleteOwn; + else if ([roles containsObject: MAPIStoreRightDeleteOwn]) + rights |= RightsDeleteOwn; + if ([roles containsObject: SOGoRole_ObjectEditor]) rights |= RightsEditAll | RightsEditOwn; + else if ([roles containsObject: MAPIStoreRightEditOwn]) + rights |= RightsEditOwn; + if ([roles containsObject: SOGoRole_ObjectViewer]) rights |= RightsReadItems; if (rights != 0) rights |= RoleNone; /* actually "folder visible" */ + if ([roles containsObject: MAPIStoreRightFolderOwner]) + rights |= RightsFolderOwner | RoleNone; + + if ([roles containsObject: MAPIStoreRightFolderContact]) + rights |= RightsFolderContact; + return rights; } diff --git a/OpenChange/MAPIStoreContactsMessage.m b/OpenChange/MAPIStoreContactsMessage.m index 4befdfd5d..344f31c39 100644 --- a/OpenChange/MAPIStoreContactsMessage.m +++ b/OpenChange/MAPIStoreContactsMessage.m @@ -31,9 +31,11 @@ #import #import #import +#import #import #import #import +#import #import #import "MAPIStoreAttachment.h" @@ -1224,23 +1226,28 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 } // --------------------------------------------------------- +// Permissions +// --------------------------------------------------------- + +- (NSString *) creator +{ + return [[[sogoObject vCard] uniqueChildWithTag: @"x-openchange-creator"] + flattenedValuesForKey: @""]; +} + +- (NSString *) owner +{ + return [self creator]; +} - (BOOL) subscriberCanReadMessage { return [[self activeUserRoles] containsObject: SOGoRole_ObjectViewer]; } -- (BOOL) subscriberCanModifyMessage -{ - NSArray *roles; - - roles = [self activeUserRoles]; - - return ((isNew - && [roles containsObject: SOGoRole_ObjectCreator]) - || (!isNew && [roles containsObject: SOGoRole_ObjectEditor])); -} - +// --------------------------------------------------------- +// Save +// --------------------------------------------------------- - (void) saveDistList:(TALLOC_CTX *) memCtx { [self warnWithFormat: @"IPM.DistList messages are ignored"]; @@ -1584,6 +1591,14 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 if (value) [newCard setNote: value]; + /* Store the creator name for sharing purposes */ + if (isNew) + { + value = [[[self context] activeUser] login]; + [[newCard uniqueChildWithTag: @"x-openchange-creator"] + setSingleValue: value forKey: @""]; + } + // // we save the new/modified card // diff --git a/OpenChange/MAPIStoreContext.m b/OpenChange/MAPIStoreContext.m index f4170a6f7..32a01e147 100644 --- a/OpenChange/MAPIStoreContext.m +++ b/OpenChange/MAPIStoreContext.m @@ -438,8 +438,16 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) mapiStoreObjectWithSOGoObject: currentFolder inContainer: nil]; [baseFolder setContext: self]; - *folderPtr = baseFolder; - rc = MAPISTORE_SUCCESS; + + if ([[userContext sogoUser] isEqual: activeUser] + || [baseFolder subscriberCanReadMessages]) + { + *folderPtr = baseFolder; + rc = MAPISTORE_SUCCESS; + } + else + rc = MAPISTORE_ERR_DENIED; + } else if ([[userContext sogoUser] isEqual: activeUser]) rc = MAPISTORE_ERR_NOT_FOUND; @@ -494,8 +502,11 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) mappingId = [mapping idFromURL: childURL]; if (mappingId == NSNotFound) { + const char *owner; + [self logWithFormat: @"No id exist yet for '%@', requesting one", childURL]; - ret = mapistore_indexing_get_new_folderID (connInfo->mstore_ctx, &mappingId); + owner = [[userContext username] UTF8String]; + ret = mapistore_indexing_get_new_folderID_as_user (connInfo->mstore_ctx, owner, &mappingId); if (ret == MAPISTORE_SUCCESS) [mapping registerURL: childURL withID: mappingId]; else @@ -506,36 +517,56 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) return mappingId; } +/* Get new change number from openchange db interface using + resource's owner user */ - (uint64_t) getNewChangeNumber { + const char *owner; + enum MAPISTATUS retval; uint64_t newVersionNumber; - if (openchangedb_get_new_changeNumber (connInfo->oc_ctx, connInfo->username, &newVersionNumber) - != MAPI_E_SUCCESS) - abort (); + owner = [[userContext username] UTF8String]; + retval = openchangedb_get_new_changeNumber (connInfo->oc_ctx, owner, &newVersionNumber); + if (retval != MAPI_E_SUCCESS) + [NSException raise: @"MAPIStoreIOException" + format: @"Impossible to get new change number for %s: %s", owner, + mapi_get_errstr (retval)]; return newVersionNumber; } +/* Get new change numbers from openchange db interface using + resource's owner user */ - (NSArray *) getNewChangeNumbers: (uint64_t) max { + const char *owner; + enum MAPISTATUS retval; TALLOC_CTX *memCtx; NSMutableArray *newChangeNumbers; uint64_t count; struct UI8Array_r *numbers; NSString *newNumber; - memCtx = talloc_zero(NULL, TALLOC_CTX); - newChangeNumbers = [NSMutableArray arrayWithCapacity: max]; + memCtx = talloc_new (NULL); + if (!memCtx) + [NSException raise: @"MAPIStoreIOException" + format: @"Not enough memory to allocate change numbers"]; + + newChangeNumbers = [NSMutableArray arrayWithCapacity: max]; + owner = [[userContext username] UTF8String]; + + retval = openchangedb_get_new_changeNumbers (connInfo->oc_ctx, memCtx, owner, max, &numbers); + if (retval != MAPI_E_SUCCESS || numbers->cValues != max) + { + talloc_free (memCtx); + [NSException raise: @"MAPIStoreIOException" + format: @"Failing to get %d new change numbers: %s", max, + mapi_get_errstr (retval)]; + } - if (openchangedb_get_new_changeNumbers (connInfo->oc_ctx, - memCtx, connInfo->username, max, &numbers) - != MAPI_E_SUCCESS || numbers->cValues != max) - abort (); for (count = 0; count < max; count++) { - newNumber - = [NSString stringWithUnsignedLongLong: numbers->lpui8[count]]; + newNumber = [NSString stringWithUnsignedLongLong: numbers->lpui8[count]]; [newChangeNumbers addObject: newNumber]; } @@ -544,29 +575,31 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) return newChangeNumbers; } +/* Get new fmids from mapistore_indexing interface using resource's + owner user */ - (NSArray *) getNewFMIDs: (uint64_t) max { - TALLOC_CTX *memCtx; + const char *owner; + enum mapistore_error ret; NSMutableArray *newFMIDs; - uint64_t count; - struct UI8Array_r *numbers; NSString *newNumber; + uint64_t count, newFID; - memCtx = talloc_zero(NULL, TALLOC_CTX); newFMIDs = [NSMutableArray arrayWithCapacity: max]; + /* Get the resource's owner name */ + owner = [[userContext username] UTF8String]; - if (mapistore_indexing_get_new_folderIDs (connInfo->mstore_ctx, - memCtx, max, &numbers) - != MAPISTORE_SUCCESS || numbers->cValues != max) - abort (); for (count = 0; count < max; count++) { - newNumber = [NSString stringWithUnsignedLongLong: numbers->lpui8[count]]; + ret = mapistore_indexing_get_new_folderID_as_user (connInfo->mstore_ctx, owner, &newFID); + if (ret != MAPISTORE_SUCCESS) + [NSException raise: @"MAPIStoreIOException" + format: @"Impossible to get new fmid for %s", owner]; + + newNumber = [NSString stringWithUnsignedLongLong: newFID]; [newFMIDs addObject: newNumber]; } - talloc_free (memCtx); - return newFMIDs; } diff --git a/OpenChange/MAPIStoreDBFolder.h b/OpenChange/MAPIStoreDBFolder.h index 2415af934..1da4d1bd9 100644 --- a/OpenChange/MAPIStoreDBFolder.h +++ b/OpenChange/MAPIStoreDBFolder.h @@ -25,6 +25,16 @@ #import "MAPIStoreFolder.h" +extern NSString *MAPIStoreRightReadItems; +extern NSString *MAPIStoreRightCreateItems; +extern NSString *MAPIStoreRightEditOwn; +extern NSString *MAPIStoreRightEditAll; +extern NSString *MAPIStoreRightDeleteOwn; +extern NSString *MAPIStoreRightDeleteAll; +extern NSString *MAPIStoreRightCreateSubfolders; +extern NSString *MAPIStoreRightFolderOwner; +extern NSString *MAPIStoreRightFolderContact; + @interface MAPIStoreDBFolder : MAPIStoreFolder @end diff --git a/OpenChange/MAPIStoreDBFolder.m b/OpenChange/MAPIStoreDBFolder.m index a7c49b72d..4fc954112 100644 --- a/OpenChange/MAPIStoreDBFolder.m +++ b/OpenChange/MAPIStoreDBFolder.m @@ -51,16 +51,6 @@ 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 @@ -355,8 +345,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; - (BOOL) subscriberCanModifyMessages { - return ([self _testRoleForActiveUser: MAPIStoreRightEditAll] - || [self _testRoleForActiveUser: MAPIStoreRightEditOwn]); + return [self _testRoleForActiveUser: MAPIStoreRightEditAll]; } - (BOOL) subscriberCanReadMessages @@ -377,8 +366,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; - (BOOL) subscriberCanDeleteMessages { - return ([self _testRoleForActiveUser: MAPIStoreRightDeleteAll] - || [self _testRoleForActiveUser: MAPIStoreRightDeleteOwn]); + return [self _testRoleForActiveUser: MAPIStoreRightDeleteAll]; } - (BOOL) subscriberCanCreateSubFolders diff --git a/OpenChange/MAPIStoreDBMessage.m b/OpenChange/MAPIStoreDBMessage.m index a397f7521..ca2b772d0 100644 --- a/OpenChange/MAPIStoreDBMessage.m +++ b/OpenChange/MAPIStoreDBMessage.m @@ -27,6 +27,9 @@ #import #import #import +#import +#import +#import #import "MAPIStoreContext.h" #import "MAPIStorePropertySelectors.h" @@ -346,6 +349,16 @@ /* Update PredecessorChangeList accordingly */ [self _updatePredecessorChangeList]; + if (isNew) + { + NSString *lastModifierName; + + lastModifierName = (NSString *)[properties objectForKey: MAPIPropertyKey (PidTagLastModifierName)]; + if ([lastModifierName length] > 0) + [properties setObject: lastModifierName + forKey: MAPIPropertyKey (PidTagCreatorName)]; + } + // [self logWithFormat: @"Saving %@", [self description]]; // [self logWithFormat: @"%d props in dict", [properties count]]; @@ -364,20 +377,77 @@ return [msgClass isEqualToString: @"IPM.Microsoft.ScheduleData.FreeBusy"]; } -/* TODO: differentiate between the "Own" and "All" cases */ +//----------------------------- +// Permissions +//----------------------------- + - (BOOL) subscriberCanReadMessage { return [(MAPIStoreFolder *) container subscriberCanReadMessages]; - // || [self _messageIsFreeBusy]); +} + +- (SOGoUser *) _ownerUser +{ + NSString *ownerName; + SOGoUser *ownerUser = nil; + + ownerName = [properties objectForKey: MAPIPropertyKey (PidTagCreatorName)]; + if ([ownerName length] > 0) + ownerUser = [SOGoUser userWithLogin: ownerName]; + + return ownerUser; +} + +- (NSArray *) activeUserRoles +{ + /* Override because of this exception: NSInvalidArgumentException, + reason: [SOGoMAPIDBMessage-aclsForUser:] should be overridden by + subclass */ + if (!activeUserRoles) + { + SOGoUser *activeUser; + + activeUser = [[self context] activeUser]; + activeUserRoles = [[container aclFolder] aclsForUser: [activeUser login]]; + [activeUserRoles retain]; + } + + return activeUserRoles; } - (BOOL) subscriberCanModifyMessage { - return ((isNew - && [(MAPIStoreFolder *) container subscriberCanCreateMessages]) - || (!isNew - && [(MAPIStoreFolder *) container subscriberCanModifyMessages])); - // || [self _messageIsFreeBusy]); + BOOL rc; + NSArray *roles; + + roles = [self activeUserRoles]; + + if (isNew) + rc = [(MAPIStoreFolder *) container subscriberCanCreateMessages]; + else + rc = [roles containsObject: MAPIStoreRightEditAll]; + + /* Check if the message is owned and it has permission to edit it */ + if (!rc && [roles containsObject: MAPIStoreRightEditOwn]) + rc = [[[container context] activeUser] isEqual: [self _ownerUser]]; + + return rc; +} + +- (BOOL) subscriberCanDeleteMessage +{ + BOOL rc; + NSArray *roles; + + roles = [self activeUserRoles]; + + rc = [roles containsObject: MAPIStoreRightDeleteAll]; + + /* Check if the message is owned and it has permission to delete it */ + if (!rc && [roles containsObject: MAPIStoreRightDeleteOwn]) + rc = [[[container context] activeUser] isEqual: [self _ownerUser]]; + + return rc; } - (NSDate *) creationTime diff --git a/OpenChange/MAPIStoreFAIMessage.m b/OpenChange/MAPIStoreFAIMessage.m index bc5608912..1c0f1b9a0 100644 --- a/OpenChange/MAPIStoreFAIMessage.m +++ b/OpenChange/MAPIStoreFAIMessage.m @@ -24,6 +24,7 @@ #import "MAPIStoreActiveTables.h" #import "MAPIStoreContext.h" +#import "MAPIStoreFolder.h" #import "MAPIStoreUserContext.h" #import "NSObject+MAPIStore.h" @@ -69,7 +70,7 @@ - (BOOL) subscriberCanReadMessage { - return NO; + return [(MAPIStoreFolder *)container subscriberCanReadMessages]; } - (BOOL) subscriberCanModifyMessage diff --git a/OpenChange/MAPIStoreFolder.h b/OpenChange/MAPIStoreFolder.h index ab0a56da5..46e13d235 100644 --- a/OpenChange/MAPIStoreFolder.h +++ b/OpenChange/MAPIStoreFolder.h @@ -22,6 +22,7 @@ #define MAPISTOREFOLDER_H #import +#import @class NSArray; @class NSMutableArray; @@ -41,6 +42,20 @@ #import "MAPIStoreSOGoObject.h" +/* MAPI Permissions + + This set has only sogo-openchange library scope + */ +extern NSString *MAPIStoreRightReadItems; +extern NSString *MAPIStoreRightCreateItems; +extern NSString *MAPIStoreRightEditOwn; +extern NSString *MAPIStoreRightEditAll; +extern NSString *MAPIStoreRightDeleteOwn; +extern NSString *MAPIStoreRightDeleteAll; +extern NSString *MAPIStoreRightCreateSubfolders; +extern NSString *MAPIStoreRightFolderOwner; +extern NSString *MAPIStoreRightFolderContact; + @interface MAPIStoreFolder : MAPIStoreSOGoObject { MAPIStoreContext *context; @@ -74,6 +89,8 @@ - (MAPIStorePermissionsTable *) permissionsTable; - (NSArray *) permissionEntries; +- (NSArray *) expandRoles: (NSArray *) roles; + /* message objects and tables */ - (id) lookupMessage: (NSString *) messageKey; - (NSArray *) messageKeys; diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index 6ffd5da00..43efcaff9 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -65,6 +65,17 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMessageTableK, MAPIStoreFolderTableK; +/* MAPI permissions */ +NSString *MAPIStoreRightReadItems = @"RightsReadItems"; +NSString *MAPIStoreRightCreateItems = @"RightsCreateItems"; +NSString *MAPIStoreRightEditOwn = @"RightsEditOwn"; +NSString *MAPIStoreRightEditAll = @"RightsEditAll"; +NSString *MAPIStoreRightDeleteOwn = @"RightsDeleteOwn"; +NSString *MAPIStoreRightDeleteAll = @"RightsDeleteAll"; +NSString *MAPIStoreRightCreateSubfolders = @"RightsCreateSubfolders"; +NSString *MAPIStoreRightFolderOwner = @"RightsFolderOwner"; +NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; + @implementation MAPIStoreFolder + (void) initialize @@ -597,7 +608,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe if ([[context activeUser] isEqual: ownerUser] || (![message isKindOfClass: MAPIStoreFAIMessageK] - && [self subscriberCanDeleteMessages])) + && ([self subscriberCanDeleteMessages] || [message subscriberCanDeleteMessage]))) { /* we ensure the table caches are loaded so that old and new state can be compared */ @@ -908,6 +919,11 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe return nil; } +- (NSArray *) expandRoles: (NSArray *) roles +{ + return roles; +} + - (void) _modifyPermissionEntryForUser: (NSString *) user withRoles: (NSArray *) roles isAddition: (BOOL) isAddition @@ -1021,9 +1037,10 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe tableType: (enum mapistore_table_type) tableType andHandleId: (uint32_t) handleId { + BOOL access; enum mapistore_error rc = MAPISTORE_SUCCESS; MAPIStoreTable *table; - SOGoUser *ownerUser; + SOGoUser *activeUser, *ownerUser; if (tableType == MAPISTORE_MESSAGE_TABLE) table = [self messageTable]; @@ -1034,8 +1051,20 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe else if (tableType == MAPISTORE_PERMISSIONS_TABLE) { ownerUser = [[self userContext] sogoUser]; - if ([[context activeUser] isEqual: ownerUser]) - table = [self permissionsTable]; + activeUser = [context activeUser]; + access = [activeUser isEqual: ownerUser]; + if (!access) + { + NSArray *roles; + + roles = [[self aclFolder] aclsForUser: [activeUser login]]; + roles = [self expandRoles: roles]; // Not required here + /* Check FolderVisible right to return the table */ + access = ([self exchangeRightsForRoles: roles] & RoleNone) != 0; + } + + if (access) + table = [self permissionsTable]; else rc = MAPISTORE_ERR_DENIED; } @@ -1267,25 +1296,29 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe inMemCtx: (TALLOC_CTX *) memCtx { uint32_t rights = 0; - SOGoUser *ownerUser; - BOOL userIsOwner; + SOGoUser *activeUser, *ownerUser; ownerUser = [[self userContext] sogoUser]; + activeUser = [context activeUser]; - userIsOwner = [[context activeUser] isEqual: ownerUser]; - if (userIsOwner || [self subscriberCanReadMessages]) - rights |= RightsReadItems; - if (userIsOwner || [self subscriberCanCreateMessages]) - rights |= RightsCreateItems; - if (userIsOwner || [self subscriberCanModifyMessages]) - rights |= RightsEditOwn | RightsEditAll; - if (userIsOwner || [self subscriberCanDeleteMessages]) - rights |= RightsDeleteOwn | RightsDeleteAll; - if ((userIsOwner || [self subscriberCanCreateSubFolders]) - && [self supportsSubFolders]) - rights |= RightsCreateSubfolders; - if (userIsOwner) - rights |= RightsFolderOwner | RightsFolderContact; + if ([activeUser isEqual: ownerUser]) + { + rights = RightsReadItems | RightsCreateItems | RightsEditOwn | RightsEditAll + | RightsDeleteOwn | RightsDeleteAll | RightsFolderOwner | RightsFolderContact | RoleNone; + if ([self supportsSubFolders]) + rights |= RightsCreateSubfolders; + } + else + { + NSArray *roles; + + roles = [[self aclFolder] aclsForUser: [activeUser login]]; + roles = [self expandRoles: roles]; + rights = [self exchangeRightsForRoles: roles]; + /* FreeBusySimple and FreeBusyDetailed does not apply here + [MS-OXCFOLD] Section 2.2.2.2.2.8 */ + rights &= ~RightsFreeBusySimple & ~RightsFreeBusyDetailed; + } *data = MAPILongValue (memCtx, rights); @@ -1626,6 +1659,22 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe NSArray *permissionRoles; BOOL reset, isAdd = NO, isDelete = NO, isModify = NO; SOGoFolder *aclFolder; + SOGoUser *activeUser, *ownerUser; + + /* Check if we have permissions to modify the permissions. + See [MS-OXCPERM] Section 3.2.5.2 for details */ + ownerUser = [[self userContext] sogoUser]; + activeUser = [context activeUser]; + if (![activeUser isEqual: ownerUser]) + { + /* Check if we have FolderOwner right */ + NSArray *roles; + + roles = [[self aclFolder] aclsForUser: [activeUser login]]; + roles = [self expandRoles: roles]; // Not required + if (([self exchangeRightsForRoles: roles] & RightsFolderOwner) == 0) + return MAPISTORE_ERR_DENIED; + } aclFolder = [self aclFolder]; diff --git a/OpenChange/MAPIStoreGCSFolder.m b/OpenChange/MAPIStoreGCSFolder.m index c70e1f1c3..9b269df35 100644 --- a/OpenChange/MAPIStoreGCSFolder.m +++ b/OpenChange/MAPIStoreGCSFolder.m @@ -859,6 +859,7 @@ static Class NSNumberK; woContext = [[self userContext] woContext]; activeUserRoles = [activeUser rolesForObject: sogoObject inContext: woContext]; + activeUserRoles = [self expandRoles: activeUserRoles]; [activeUserRoles retain]; } diff --git a/OpenChange/MAPIStoreGCSMessage.h b/OpenChange/MAPIStoreGCSMessage.h index cac182874..2cfd480d0 100644 --- a/OpenChange/MAPIStoreGCSMessage.h +++ b/OpenChange/MAPIStoreGCSMessage.h @@ -23,6 +23,8 @@ #ifndef MAPISTOREGCSMESSAGE_H #define MAPISTOREGCSMESSAGE_H +#import + #import "MAPIStoreMessage.h" @interface MAPIStoreGCSMessage : MAPIStoreMessage @@ -30,6 +32,11 @@ } /* subclass helpers */ + +/* Return the message original creator */ +- (NSString *) creator; +- (NSString *) owner; +- (SOGoUser *) ownerUser; - (void) updateVersions; @end diff --git a/OpenChange/MAPIStoreGCSMessage.m b/OpenChange/MAPIStoreGCSMessage.m index 84976917e..e1792fbdf 100644 --- a/OpenChange/MAPIStoreGCSMessage.m +++ b/OpenChange/MAPIStoreGCSMessage.m @@ -20,10 +20,12 @@ * Boston, MA 02111-1307, USA. */ +#import #import #import #import #import +#import #import #import #import @@ -35,6 +37,7 @@ #import "MAPIStoreTypes.h" #import "MAPIStoreUserContext.h" #import "NSData+MAPIStore.h" +#import "NSString+MAPIStore.h" #import "MAPIStoreGCSMessage.h" @@ -54,70 +57,22 @@ return [sogoObject lastModified]; } -- (enum mapistore_error) getPidTagAccess: (void **) data // TODO - inMemCtx: (TALLOC_CTX *) memCtx -{ - MAPIStoreContext *context; - WOContext *woContext; - SoSecurityManager *sm; - MAPIStoreUserContext *userContext; - uint32_t access; - - context = [self context]; - userContext = [self userContext]; - if ([[context activeUser] isEqual: [userContext sogoUser]]) - access = 0x03; - else - { - sm = [SoSecurityManager sharedSecurityManager]; - woContext = [userContext woContext]; - - access = 0; - if (![sm validatePermission: SoPerm_ChangeImagesAndFiles - onObject: sogoObject - inContext: woContext]) - access |= 1; - if (![sm validatePermission: SoPerm_AccessContentsInformation - onObject: sogoObject - inContext: woContext]) - access |= 2; - if (![sm validatePermission: SOGoPerm_DeleteObject - onObject: sogoObject - inContext: woContext]) - access |= 4; - } - *data = MAPILongValue (memCtx, access); - - return MAPISTORE_SUCCESS; -} - -- (enum mapistore_error) getPidTagAccessLevel: (void **) data // TODO +- (enum mapistore_error) getPidTagCreatorName: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - MAPIStoreContext *context; - MAPIStoreUserContext *userContext; - WOContext *woContext; - SoSecurityManager *sm; - uint32_t accessLvl; + enum mapistore_error rc; + NSString *creator; - context = [self context]; - userContext = [self userContext]; - if ([[context activeUser] isEqual: [userContext sogoUser]]) - accessLvl = 1; - else + creator = [self creator]; + if (creator) { - sm = [SoSecurityManager sharedSecurityManager]; - woContext = [userContext woContext]; - if (![sm validatePermission: SoPerm_ChangeImagesAndFiles - onObject: sogoObject - inContext: woContext]) - accessLvl = 1; - else - accessLvl = 0; + *data = [creator asUnicodeInMemCtx: memCtx]; + rc = MAPISTORE_SUCCESS; } - *data = MAPILongValue (memCtx, accessLvl); + else + rc = MAPISTORE_ERR_NOT_FOUND; - return MAPISTORE_SUCCESS; + return rc; } - (enum mapistore_error) getPidTagChangeKey: (void **) data @@ -241,4 +196,69 @@ andPredecessorChangeList: predecessorChangeList]; } +//---------------------- +// Sharing +//---------------------- + +- (NSString *) creator +{ + return [self owner]; +} + +- (NSString *) owner +{ + return [sogoObject ownerInContext: nil]; +} + +- (SOGoUser *) ownerUser +{ + NSString *ownerName; + SOGoUser *owner = nil; + + ownerName = [self owner]; + if ([ownerName length] != 0) + owner = [SOGoUser userWithLogin: ownerName]; + + return owner; +} + +- (BOOL) subscriberCanModifyMessage +{ + BOOL rc; + NSArray *roles; + + roles = [self activeUserRoles]; + + if (isNew) + rc = [roles containsObject: SOGoRole_ObjectCreator]; + else + rc = [roles containsObject: SOGoRole_ObjectEditor]; + + /* Check if the message is owned and it has permission to edit it */ + if (!rc && [roles containsObject: MAPIStoreRightEditOwn]) + rc = [[[container context] activeUser] isEqual: [self ownerUser]]; + + return rc; +} + +- (BOOL) subscriberCanDeleteMessage +{ + BOOL rc; + NSArray *roles; + + roles = [self activeUserRoles]; + rc = [roles containsObject: SOGoRole_ObjectEraser]; + + /* Check if the message is owned and it has permission to delete it */ + if (!rc && [roles containsObject: MAPIStoreRightDeleteOwn]) + { + NSString *currentUser; + + currentUser = [[container context] activeUser]; + rc = [currentUser isEqual: [self ownerUser]]; + } + + return rc; +} + @end diff --git a/OpenChange/MAPIStoreMapping.m b/OpenChange/MAPIStoreMapping.m index b7d303ed6..2e2c9a66d 100644 --- a/OpenChange/MAPIStoreMapping.m +++ b/OpenChange/MAPIStoreMapping.m @@ -258,7 +258,7 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr) if (oldURL != NULL) { [self errorWithFormat: - @"url with idNbr already registered: (oldUrl='%@', newUrl='%@', id=x%.16"PRIx64")", + @"url with idNbr already registered: (oldUrl='%@', newUrl='%@', id=0x%.16"PRIx64")", oldURL, urlString, idNbr]; return NO; } @@ -306,7 +306,7 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr) } else [NSException raise: NSInvalidArgumentException - format: @"number of urls and ids do not match"]; + format: @"number of urls (%d) and ids (%d) do not match", max, [idNbrs count]]; } - (void) unregisterURLWithID: (uint64_t) idNbr diff --git a/OpenChange/MAPIStoreMessage.h b/OpenChange/MAPIStoreMessage.h index fd89bb027..b7441a74c 100644 --- a/OpenChange/MAPIStoreMessage.h +++ b/OpenChange/MAPIStoreMessage.h @@ -69,6 +69,7 @@ - (NSArray *) activeContainerMessageTables; - (NSArray *) activeUserRoles; +- (NSArray *) expandRoles: (NSArray *) roles; /* move & copy internal ops */ - (void) copyToMessage: (MAPIStoreMessage *) newMessage inMemCtx: (TALLOC_CTX *) memCtx; @@ -82,6 +83,7 @@ - (BOOL) subscriberCanReadMessage; - (BOOL) subscriberCanModifyMessage; +- (BOOL) subscriberCanDeleteMessage; @end diff --git a/OpenChange/MAPIStoreMessage.m b/OpenChange/MAPIStoreMessage.m index d3ba9aae7..3e303e5a3 100644 --- a/OpenChange/MAPIStoreMessage.m +++ b/OpenChange/MAPIStoreMessage.m @@ -547,7 +547,8 @@ rtf2html (NSData *compressedRTF) for (count = 0; count < max; count++) [[containerTables objectAtIndex: count] restrictedChildKeys]; } - + + /* TODO: Check the save process succeeded */ [self save: memCtx]; /* We make sure that any change-related properties are removed from the properties dictionary, to make sure that related methods will be @@ -649,8 +650,7 @@ rtf2html (NSData *compressedRTF) if (userIsOwner || ([self isKindOfClass: MAPIStoreEmbeddedMessageK] && [mainMessage subscriberCanModifyMessage]) - || [(MAPIStoreFolder *) - [mainMessage container] subscriberCanDeleteMessages]) + || [mainMessage subscriberCanDeleteMessage]) access |= 0x04; *data = MAPILongValue (memCtx, access); @@ -981,20 +981,38 @@ rtf2html (NSData *compressedRTF) activeUserRoles = [[context activeUser] rolesForObject: sogoObject inContext: [userContext woContext]]; + /* We use in this library the roles as flags, so we expand high + access rights with the lower ones */ + activeUserRoles = [self expandRoles: activeUserRoles]; [activeUserRoles retain]; } return activeUserRoles; } +/* Expand current roles with lower access roles to transform them to + flags */ +- (NSArray *) expandRoles: (NSArray *) roles +{ + return roles; +} + +/* Can the current active user read the message? */ - (BOOL) subscriberCanReadMessage { return NO; } +/* Can the current active user modify the message? */ - (BOOL) subscriberCanModifyMessage { return NO; } +/* Can the current active user delete the message? */ +- (BOOL) subscriberCanDeleteMessage +{ + return NO; +} + @end diff --git a/OpenChange/MAPIStoreNotesContext.m b/OpenChange/MAPIStoreNotesContext.m index 725b0d158..f5b8b1be3 100644 --- a/OpenChange/MAPIStoreNotesContext.m +++ b/OpenChange/MAPIStoreNotesContext.m @@ -1,6 +1,7 @@ /* MAPIStoreNotesContext.m - this file is part of SOGo * * Copyright (C) 2010-2012 Inverse inc. + * Copyright (C) 2016 Enrique J. Hernandez * * Author: Wolfgang Sourdeau * @@ -30,13 +31,30 @@ #undef DEBUG #include +static Class MAPIStoreNotesFolderK; + @implementation MAPIStoreNotesContext ++ (void) initialize +{ + MAPIStoreNotesFolderK = [MAPIStoreNotesFolder class]; +} + + (NSString *) MAPIModuleName { return @"notes"; } ++ (enum mapistore_context_role) MAPIContextRole +{ + return MAPISTORE_NOTES_ROLE; +} + +- (Class) MAPIStoreFolderClass +{ + return MAPIStoreNotesFolderK; +} + + (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName withIndexing: (struct indexing_context *) indexing inMemCtx: (TALLOC_CTX *) memCtx diff --git a/OpenChange/MAPIStorePermissionsTable.m b/OpenChange/MAPIStorePermissionsTable.m index 193ef9a77..3bd2f84b7 100644 --- a/OpenChange/MAPIStorePermissionsTable.m +++ b/OpenChange/MAPIStorePermissionsTable.m @@ -134,6 +134,7 @@ NSArray *roles; roles = [[(MAPIStoreFolder *) container aclFolder] aclsForUser: userId]; + roles = [(MAPIStoreFolder *) container expandRoles: roles]; rights = [(MAPIStoreFolder *) container exchangeRightsForRoles: roles]; *data = MAPILongValue (memCtx, rights); diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index 41c8bf42a..604482ee7 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -244,8 +244,13 @@ static void mapiapp_cleanup(void) \details Create a connection context to the sogo backend \param mem_ctx pointer to the memory context + \param conn_info pointer to the connection information available for this context + (database connection, connected user, replica server info) + \param indexing pointer to the indexing database connection \param uri pointer to the sogo path \param private_data pointer to the private backend context + + \note the developer must free allocated private_data */ static enum mapistore_error diff --git a/OpenChange/MAPIStoreTasksFolder.h b/OpenChange/MAPIStoreTasksFolder.h index dc9b6791a..3d16a44ae 100644 --- a/OpenChange/MAPIStoreTasksFolder.h +++ b/OpenChange/MAPIStoreTasksFolder.h @@ -23,9 +23,9 @@ #ifndef MAPISTORETASKSFOLDER_H #define MAPISTORETASKSFOLDER_H -#import "MAPIStoreGCSFolder.h" +#import "MAPIStoreCalTaskFolder.h" -@interface MAPIStoreTasksFolder : MAPIStoreGCSFolder +@interface MAPIStoreTasksFolder : MAPIStoreCalTaskFolder @end diff --git a/OpenChange/MAPIStoreTasksFolder.m b/OpenChange/MAPIStoreTasksFolder.m index f4f6ac160..4b198d2d1 100644 --- a/OpenChange/MAPIStoreTasksFolder.m +++ b/OpenChange/MAPIStoreTasksFolder.m @@ -75,6 +75,10 @@ return newMessage; } +// -------------------------------------------- +// Permissions and sharing +// -------------------------------------------- + - (NSArray *) rolesForExchangeRights: (uint32_t) rights { NSMutableArray *roles; @@ -82,21 +86,34 @@ roles = [NSMutableArray arrayWithCapacity: 6]; if (rights & RightsCreateItems) [roles addObject: SOGoRole_ObjectCreator]; + if (rights & RightsDeleteAll) [roles addObject: SOGoRole_ObjectEraser]; + if (rights & RightsDeleteOwn) + [roles addObject: MAPIStoreRightDeleteOwn]; + if (rights & RightsEditAll) { [roles addObject: SOGoCalendarRole_PublicModifier]; [roles addObject: SOGoCalendarRole_PrivateModifier]; [roles addObject: SOGoCalendarRole_ConfidentialModifier]; } - else if (rights & RightsReadItems) + if (rights & RightsEditOwn) + [roles addObject: MAPIStoreRightEditOwn]; + + if (rights & RightsReadItems) { [roles addObject: SOGoCalendarRole_PublicViewer]; [roles addObject: SOGoCalendarRole_PrivateViewer]; [roles addObject: SOGoCalendarRole_ConfidentialViewer]; } + if (rights & RightsFolderOwner) + [roles addObject: MAPIStoreRightFolderOwner]; + + if (rights & RightsFolderContact) + [roles addObject: MAPIStoreRightFolderContact]; + return roles; } @@ -116,20 +133,65 @@ && [roles containsObject: SOGoCalendarRole_PrivateViewer] && [roles containsObject: SOGoCalendarRole_ConfidentialViewer]) rights |= RightsReadItems; + + if ([roles containsObject: MAPIStoreRightEditOwn]) + rights |= RightsEditOwn; + if ([roles containsObject: MAPIStoreRightDeleteOwn]) + rights |= RightsDeleteOwn; + if (rights != 0) rights |= RoleNone; /* actually "folder visible" */ + if ([roles containsObject: MAPIStoreRightFolderOwner]) + rights |= RightsFolderOwner | RoleNone; + + if ([roles containsObject: MAPIStoreRightFolderContact]) + rights |= RightsFolderContact; + return rights; } +- (BOOL) subscriberCanModifyMessages +{ + static NSArray *modifierRoles = nil; + + if (!modifierRoles) + modifierRoles = [[NSArray alloc] initWithObjects: + SOGoCalendarRole_PublicModifier, + SOGoCalendarRole_PrivateModifier, + SOGoCalendarRole_ConfidentialModifier, + nil]; + + return ([[self activeUserRoles] firstObjectCommonWithArray: modifierRoles] + != nil); +} + +- (BOOL) subscriberCanReadMessages +{ + static NSArray *viewerRoles = nil; + + if (!viewerRoles) + viewerRoles = [[NSArray alloc] initWithObjects: + SOGoCalendarRole_PublicViewer, + SOGoCalendarRole_PrivateViewer, + SOGoCalendarRole_ConfidentialViewer, + nil]; + + return ([[self activeUserRoles] firstObjectCommonWithArray: viewerRoles] + != nil); +} + - (EOQualifier *) aclQualifier { return [EOQualifier qualifierWithQualifierFormat: [(SOGoAppointmentFolder *) sogoObject aclSQLListingFilter]]; } +// -------------------------------------------- +// Property getters +// -------------------------------------------- - (enum mapistore_error) getPidTagDefaultPostMessageClass: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { *data = [@"IPM.Task" asUnicodeInMemCtx: memCtx]; diff --git a/OpenChange/MAPIStoreTasksMessage.h b/OpenChange/MAPIStoreTasksMessage.h index 3130b72a5..360c516ad 100644 --- a/OpenChange/MAPIStoreTasksMessage.h +++ b/OpenChange/MAPIStoreTasksMessage.h @@ -23,9 +23,9 @@ #ifndef MAPISTORETASKSMESSAGE_H #define MAPISTORETASKSMESSAGE_H -#import "MAPIStoreGCSMessage.h" +#import "MAPIStoreCalTaskMessage.h" -@interface MAPIStoreTasksMessage : MAPIStoreGCSMessage +@interface MAPIStoreTasksMessage : MAPIStoreCalTaskMessage @end diff --git a/OpenChange/MAPIStoreTasksMessage.m b/OpenChange/MAPIStoreTasksMessage.m index 728fdb909..3cd3f1bfd 100644 --- a/OpenChange/MAPIStoreTasksMessage.m +++ b/OpenChange/MAPIStoreTasksMessage.m @@ -129,12 +129,23 @@ task = [sogoObject component: NO secure: YES]; - if ([task symbolicAccessClass] == iCalAccessPublic) + if ([task isPublic]) return [self getNo: data inMemCtx: memCtx]; return [self getYes: data inMemCtx: memCtx]; } +- (enum mapistore_error) getPidTagSensitivity: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + uint32_t v; + + v = (uint32_t) [self sensitivity]; + + *data = MAPILongValue (memCtx, v); + return MAPISTORE_SUCCESS; +} + - (enum mapistore_error) getPidTagImportance: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -154,6 +165,9 @@ return MAPISTORE_SUCCESS; } +//------------------------------------ +// Specific task related properties +//------------------------------------ - (enum mapistore_error) getPidLidTaskComplete: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -323,6 +337,7 @@ { NSString *owner; + /* FIXME: This is wrong when setting task's request */ owner = [sogoObject ownerInContext: nil]; *data = [owner asUnicodeInMemCtx: memCtx]; @@ -336,25 +351,45 @@ return [self getLongZero: data inMemCtx: memCtx]; } -- (BOOL) subscriberCanReadMessage +// ---------------------------------- +// Sharing +// ---------------------------------- +- (NSUInteger) sensitivity { - return ([[self activeUserRoles] - containsObject: SOGoCalendarRole_ComponentViewer] - || [self subscriberCanModifyMessage]); + iCalToDo *task; + NSUInteger v; + + task = [sogoObject component: NO secure: YES]; + /* FIXME: Use OpenChange constants names */ + switch ([task symbolicAccessClass]) + { + case iCalAccessPrivate: + v = 0x2; + break; + case iCalAccessConfidential: + v = 0x3; + break; + default: + v = 0x0; + break; + } + return v; } -- (BOOL) subscriberCanModifyMessage +- (NSString *) creator { - BOOL rc; - NSArray *roles = [self activeUserRoles]; + iCalToDo *task; - if (isNew) - rc = [roles containsObject: SOGoRole_ObjectCreator]; - else - rc = ([roles containsObject: SOGoCalendarRole_ComponentModifier] - || [roles containsObject: SOGoCalendarRole_ComponentResponder]); + task = [sogoObject component: NO secure: YES]; + return [[task uniqueChildWithTag: @"x-sogo-component-created-by"] + flattenedValuesForKey: @""]; +} - return rc; +- (NSString *) owner +{ + /* This is not true but to allow a user edit its own tasks is required. + FIXME: When PidLidTaskOwner getter is properly implemented for Task Requests */ + return [self creator]; } - (void) save:(TALLOC_CTX *) memCtx @@ -524,10 +559,16 @@ [vToDo setAccessClass: @"PUBLIC"]; } + /* Creation */ now = [NSCalendarDate date]; if ([sogoObject isNew]) { [vToDo setCreated: now]; + /* Creator is used for sharing purposes */ + value = [properties objectForKey: MAPIPropertyKey (PidTagLastModifierName)]; + if (value) + [[vToDo uniqueChildWithTag: @"x-sogo-component-created-by"] setSingleValue: value + forKey: @""]; } [vToDo setTimeStampAsDate: now]; diff --git a/OpenChange/iCalEvent+MAPIStore.h b/OpenChange/iCalEvent+MAPIStore.h index 198526687..8d73923e1 100644 --- a/OpenChange/iCalEvent+MAPIStore.h +++ b/OpenChange/iCalEvent+MAPIStore.h @@ -37,6 +37,7 @@ - (void) updateFromMAPIProperties: (NSDictionary *) properties inUserContext: (MAPIStoreUserContext *) userContext withActiveUser: (SOGoUser *) activeUser + isNew: (BOOL) isNew inMemCtx: (TALLOC_CTX *) memCtx; @end diff --git a/OpenChange/iCalEvent+MAPIStore.m b/OpenChange/iCalEvent+MAPIStore.m index 25877029b..1bf0d261a 100644 --- a/OpenChange/iCalEvent+MAPIStore.m +++ b/OpenChange/iCalEvent+MAPIStore.m @@ -247,6 +247,7 @@ - (void) updateFromMAPIProperties: (NSDictionary *) properties inUserContext: (MAPIStoreUserContext *) userContext withActiveUser: (SOGoUser *) activeUser + isNew: (BOOL) isNew inMemCtx: (TALLOC_CTX *) memCtx { BOOL isAllDay; @@ -573,6 +574,14 @@ } } } + // Creator (with sharing purposes) + if (isNew) + { + value = [properties objectForKey: MAPIPropertyKey (PidTagLastModifierName)]; + if (value) + [[self uniqueChildWithTag: @"x-sogo-component-created-by"] setSingleValue: value + forKey: @""]; + } } @end diff --git a/UI/Scheduler/UIxCalUserRightsEditor.m b/UI/Scheduler/UIxCalUserRightsEditor.m index 00534bff8..91738707a 100644 --- a/UI/Scheduler/UIxCalUserRightsEditor.m +++ b/UI/Scheduler/UIxCalUserRightsEditor.m @@ -57,25 +57,24 @@ { NSEnumerator *roles, *types; NSString *role, *type; - unsigned int length; - roles = [userRights objectEnumerator]; - role = [roles nextObject]; - while (role) + types = [[self rightTypes] objectEnumerator]; + type = [types nextObject]; + while (type) { - types = [[self rightTypes] objectEnumerator]; - type = [types nextObject]; - while (type) - { - if ([role hasPrefix: type]) - { - length = [type length]; - [rights setObject: [role substringFromIndex: length] - forKey: type]; - } - type = [types nextObject]; - } + roles = [userRights objectEnumerator]; role = [roles nextObject]; + while (role) + { + if ([role hasPrefix: type]) + { + [rights setObject: [role substringFromIndex: [type length]] + forKey: type]; + break; + } + role = [roles nextObject]; + } + type = [types nextObject]; } }