diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 4bd77e0e5..2d9aae7b0 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -59,6 +59,7 @@ #import #import #import +#import #import "iCalCalendar+SOGo.h" #import "iCalRepeatableEntityObject+SOGo.h" @@ -70,7 +71,6 @@ #import "SOGoTaskObject.h" #import "SOGoWebAppointmentFolder.h" - #define defaultColor @"#AAAAAA" @interface SOGoGCSFolder (SOGoPrivate) @@ -437,19 +437,22 @@ static Class iCalEventK = nil; if (rc) { +#warning Duplicated code from SOGoGCSFolder subscribeUserOrGroup dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: theIdentifier]; - if ([[dict objectForKey: @"isGroup"] boolValue]) + if (dict && [[dict objectForKey: @"isGroup"] boolValue]) { - SOGoGroup *aGroup; + id source; - aGroup = [SOGoGroup groupWithIdentifier: theIdentifier - inDomain: [[context activeUser] domain]]; - allUsers = [NSMutableArray arrayWithArray: [aGroup members]]; + source = [[SOGoUserManager sharedUserManager] sourceWithID: [dict objectForKey: @"SOGoSource"]]; + if ([source conformsToProtocol:@protocol(MembershipAwareSource)]) + { + allUsers = [NSMutableArray arrayWithArray: [(id)(source) membersForGroupWithUID: [dict objectForKey: @"c_uid"]]]; - // We remove the active user from the group (if present) in order to - // not subscribe him to their own resource! - [allUsers removeObject: [context activeUser]]; + // We remove the active user from the group (if present) in order to + // not subscribe him to their own resource! + [allUsers removeObject: [context activeUser]]; + } } else { diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 97a286405..ad9959d33 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -47,7 +47,7 @@ #import #import #import -#import +#import #import #import #import @@ -1670,10 +1670,13 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent if ([event isAttendee: [[delegate email] rfc822Email]]) ex = [NSException exceptionWithHTTPStatus: 409 reason: @"delegate is a participant"]; - else if ([SOGoGroup groupWithEmail: [[delegate email] rfc822Email] - inDomain: [ownerUser domain]]) - ex = [NSException exceptionWithHTTPStatus: 409 - reason: @"delegate is a group"]; + else { + NSDictionary *dict; + dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: [[delegate email] rfc822Email]]; + if (dict && [[dict objectForKey: @"isGroup"] boolValue]) + ex = [NSException exceptionWithHTTPStatus: 409 + reason: @"delegate is a group"]; + } } if (ex == nil) { diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index d4e259943..6e5c3a8eb 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -46,7 +46,7 @@ #import #import #import -#import +#import #import #import #import @@ -525,7 +525,7 @@ iCalPerson *currentAttendee; NSEnumerator *enumerator; NSAutoreleasePool *pool; - SOGoGroup *group; + NSDictionary *dict; BOOL eventWasModified; unsigned int i, j; @@ -549,37 +549,42 @@ pool = [[NSAutoreleasePool alloc] init]; } - group = [SOGoGroup groupWithEmail: [currentAttendee rfc822Email] - inDomain: domain]; - if (group) - { + dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: [currentAttendee rfc822Email]]; + + if (dict && [[dict objectForKey: @"isGroup"] boolValue]) + { iCalPerson *person; NSArray *members; SOGoUser *user; + id source; // We did decompose a group... [allAttendees removeObject: currentAttendee]; - members = [group members]; - for (i = 0; i < [members count]; i++) - { - user = [members objectAtIndex: i]; - eventWasModified = YES; + source = [[SOGoUserManager sharedUserManager] sourceWithID: [dict objectForKey: @"SOGoSource"]]; + if ([source conformsToProtocol:@protocol(MembershipAwareSource)]) + { + members = [(id)(source) membersForGroupWithUID: [dict objectForKey: @"c_uid"]]; + for (i = 0; i < [members count]; i++) + { + user = [members objectAtIndex: i]; + eventWasModified = YES; - // If the organizer is part of the group, we skip it from - // the addition to the attendees' list - if ([user hasEmail: organizerEmail]) - continue; - - person = [self iCalPersonWithUID: [user login]]; - [person setTag: @"ATTENDEE"]; - [person setParticipationStatus: [currentAttendee participationStatus]]; - [person setRsvp: [currentAttendee rsvp]]; - [person setRole: [currentAttendee role]]; - - if (![allAttendees containsObject: person]) - [allAttendees addObject: person]; - } + // If the organizer is part of the group, we skip it from + // the addition to the attendees' list + if ([user hasEmail: organizerEmail]) + continue; + + person = [self iCalPersonWithUID: [user login]]; + [person setTag: @"ATTENDEE"]; + [person setParticipationStatus: [currentAttendee participationStatus]]; + [person setRsvp: [currentAttendee rsvp]]; + [person setRole: [currentAttendee role]]; + + if (![allAttendees containsObject: person]) + [allAttendees addObject: person]; + } + } } j++; diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m index 62a0bdb56..fd2933f22 100644 --- a/SoObjects/Mailer/SOGoMailFolder.m +++ b/SoObjects/Mailer/SOGoMailFolder.m @@ -58,7 +58,6 @@ #import #import #import -#import #import #import #import @@ -1425,15 +1424,15 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) - (NSString *) _sogoACLUIDToIMAPUID: (NSString *) uid { - SOGoGroup *group; + NSDictionary *dict; SOGoUser *user; if ([uid isEqualToString: defaultUserID]) return uid; - group = [SOGoGroup groupWithIdentifier: uid - inDomain: [[context activeUser] domain]]; - if (group) + dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: uid]; + + if (dict && [[dict objectForKey: @"isGroup"] boolValue]) return [[[[context activeUser] domainDefaults] imapAclGroupIdPrefix] stringByAppendingString: uid]; diff --git a/SoObjects/SOGo/GNUmakefile b/SoObjects/SOGo/GNUmakefile index 7148c0ac6..57c8e03a1 100644 --- a/SoObjects/SOGo/GNUmakefile +++ b/SoObjects/SOGo/GNUmakefile @@ -75,7 +75,6 @@ SOGo_HEADER_FILES = \ SOGoWebDAVAclManager.h \ SOGoWebDAVValue.h \ SOGoMailer.h \ - SOGoGroup.h \ SOGoUser.h \ \ CardElement+SOGo.h \ @@ -155,7 +154,6 @@ SOGo_OBJC_FILES = \ SOGoWebDAVAclManager.m \ SOGoWebDAVValue.m \ SOGoMailer.m \ - SOGoGroup.m \ SOGoUser.m \ \ CardElement+SOGo.m \ diff --git a/SoObjects/SOGo/LDAPSource.h b/SoObjects/SOGo/LDAPSource.h index b8fee128c..4adfda921 100644 --- a/SoObjects/SOGo/LDAPSource.h +++ b/SoObjects/SOGo/LDAPSource.h @@ -30,7 +30,7 @@ @class NSMutableDictionary; @class NSString; -@interface LDAPSource : NSObject +@interface LDAPSource : NSObject { int _queryLimit; int _queryTimeout; diff --git a/SoObjects/SOGo/LDAPSource.m b/SoObjects/SOGo/LDAPSource.m index 7afb3e92b..ea8fdc592 100644 --- a/SoObjects/SOGo/LDAPSource.m +++ b/SoObjects/SOGo/LDAPSource.m @@ -34,6 +34,7 @@ #import "NSString+Crypto.h" #import "SOGoCache.h" #import "SOGoSystemDefaults.h" +#import "SOGoUserManager.h" #import "LDAPSource.h" @@ -1990,4 +1991,138 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection, } } +#define CHECK_CLASS(o) ({ \ + if ([o isKindOfClass: [NSString class]]) \ + o = [NSArray arrayWithObject: o]; \ +}) + +- (NSArray *) membersForGroupWithUID: (NSString *) uid +{ + NSMutableArray *dns, *uids, *logins; + NSString *dn, *login; + SOGoUserManager *um; + NSDictionary *d; + SOGoUser *user; + NSArray *o; + NSAutoreleasePool *pool; + int i, c; + NGLdapEntry *entry; + NSMutableArray *members = nil; + + if ([uid hasPrefix: @"@"]) + uid = [uid substringFromIndex: 1]; + + entry = [self lookupGroupEntryByUID: uid inDomain: nil]; + + if (entry) + { + members = [NSMutableArray new]; + uids = [NSMutableArray array]; + dns = [NSMutableArray array]; + logins = [NSMutableArray array]; + + // We check if it's a static group + // Fetch "members" - we get DNs + d = [entry asDictionary]; + o = [d objectForKey: @"member"]; + CHECK_CLASS(o); + if (o) [dns addObjectsFromArray: o]; + + // Fetch "uniqueMembers" - we get DNs + o = [d objectForKey: @"uniquemember"]; + CHECK_CLASS(o); + if (o) [dns addObjectsFromArray: o]; + + // Fetch "memberUid" - we get UID (like login names) + o = [d objectForKey: @"memberuid"]; + CHECK_CLASS(o); + if (o) [uids addObjectsFromArray: o]; + + c = [dns count] + [uids count]; + + // We deal with a static group, let's add the members + if (c) + { + um = [SOGoUserManager sharedUserManager]; + + // We add members for whom we have their associated DN + for (i = 0; i < [dns count]; i++) + { + pool = [NSAutoreleasePool new]; + dn = [dns objectAtIndex: i]; + login = [um getLoginForDN: [dn lowercaseString]]; + user = [SOGoUser userWithLogin: login roles: nil]; + if (user) + { + [logins addObject: login]; + [members addObject: user]; + } + [pool release]; + } + + // We add members for whom we have their associated login name + for (i = 0; i < [uids count]; i++) + { + pool = [NSAutoreleasePool new]; + login = [uids objectAtIndex: i]; + user = [SOGoUser userWithLogin: login roles: nil]; + + if (user) + { + [logins addObject: [user loginInDomain]]; + [members addObject: user]; + } + [pool release]; + } + + + // We are done fetching members, let's cache the members of the group + // (ie., their UIDs) in memcached to speed up -hasMemberWithUID. + [[SOGoCache sharedCache] setValue: [logins componentsJoinedByString: @","] + forKey: [NSString stringWithFormat: @"%@+%@", uid, _domain]]; + } + else + { + // We deal with a dynamic group, let's search all users for whom + // memberOf is equal to our group's DN. + // We also need to look for labelelURI? + } + } + + return members; +} + +// +// +// +- (BOOL) groupWithUIDHasMemberWithUID: (NSString *) uid + memberUid: (NSString *) memberUid +{ + + BOOL rc; + NSString *key, *value;; + NSArray *a; + + rc = NO; + + if ([uid hasPrefix: @"@"]) + uid = [uid substringFromIndex: 1]; + + key = [NSString stringWithFormat: @"%@+%@", uid, _domain]; + value = [[SOGoCache sharedCache] valueForKey: key]; + + // If the value isn't in memcached, that probably means -members was never called. + // We call it only once here. + if (!value) + { + [self membersForGroupWithUID: uid]; + value = [[SOGoCache sharedCache] valueForKey: key]; + } + + a = [value componentsSeparatedByString: @","]; + rc = [a containsObject: memberUid]; + + return rc; +} + @end diff --git a/SoObjects/SOGo/SOGoCache.h b/SoObjects/SOGo/SOGoCache.h index b0fcf550b..07bdbcb6a 100644 --- a/SoObjects/SOGo/SOGoCache.h +++ b/SoObjects/SOGo/SOGoCache.h @@ -31,7 +31,6 @@ @class NGImap4Connection; -@class SOGoGroup; @class SOGoObject; @class SOGoUser; @class SOGoUserDefaults; @@ -67,13 +66,6 @@ withName: (NSString *) userName; - (id) userNamed: (NSString *) name; -- (void) registerGroup: (SOGoGroup *) group - withName: (NSString *) groupName - inDomain: (NSString *) domainName; - -- (id) groupNamed: (NSString *) groupName - inDomain: (NSString *) domainName; - - (void) registerIMAP4Connection: (NGImap4Connection *) connection forKey: (NSString *) key; - (NGImap4Connection *) imap4ConnectionForKey: (NSString *) key; diff --git a/SoObjects/SOGo/SOGoCache.m b/SoObjects/SOGo/SOGoCache.m index 7e9eea510..065f1dfe5 100644 --- a/SoObjects/SOGo/SOGoCache.m +++ b/SoObjects/SOGo/SOGoCache.m @@ -260,22 +260,6 @@ static memcached_st *handle = NULL; return [users objectForKey: name]; } -- (void) registerGroup: (SOGoGroup *) group - withName: (NSString *) groupName - inDomain: (NSString *) domainName - -{ - if (group) - [groups setObject: group forKey: [NSString stringWithFormat: @"%@+%@", groupName, domainName]]; -} - -- (id) groupNamed: (NSString *) groupName - inDomain: (NSString *) domainName - -{ - return [groups objectForKey: [NSString stringWithFormat: @"%@+%@", groupName, domainName]]; -} - - (void) registerIMAP4Connection: (NGImap4Connection *) connection forKey: (NSString *) key { diff --git a/SoObjects/SOGo/SOGoGCSFolder.m b/SoObjects/SOGo/SOGoGCSFolder.m index 68d76a5c7..f65d18ded 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -49,7 +49,7 @@ #import "SOGoCache.h" #import "SOGoContentObject.h" #import "SOGoDomainDefaults.h" -#import "SOGoGroup.h" +#import "SOGoSource.h" #import "SOGoParentFolder.h" #import "SOGoPermissions.h" #import "SOGoUser.h" @@ -933,17 +933,19 @@ static NSArray *childRecordFields = nil; dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: theIdentifier]; - if ([[dict objectForKey: @"isGroup"] boolValue]) + if (dict && [[dict objectForKey: @"isGroup"] boolValue]) { - SOGoGroup *aGroup; + id source; - aGroup = [SOGoGroup groupWithIdentifier: theIdentifier - inDomain: [[context activeUser] domain]]; - allUsers = [NSMutableArray arrayWithArray: [aGroup members]]; + source = [[SOGoUserManager sharedUserManager] sourceWithID: [dict objectForKey: @"SOGoSource"]]; + if ([source conformsToProtocol:@protocol(MembershipAwareSource)]) + { + allUsers = [NSMutableArray arrayWithArray: [(id)(source) membersForGroupWithUID: [dict objectForKey: @"c_uid"]]]; - // We remove the active user from the group (if present) in order to - // not subscribe him to their own resource! - [allUsers removeObject: [context activeUser]]; + // We remove the active user from the group (if present) in order to + // not subscribe him to their own resource! + [allUsers removeObject: [context activeUser]]; + } } else { @@ -1622,13 +1624,10 @@ static NSArray *childRecordFields = nil; { int count, max; NSDictionary *record; - NSString *currentUID, *domain; - SOGoGroup *group; + NSString *currentUID; NSMutableArray *acls; acls = [NSMutableArray array]; -#warning should it be the domain of the ownerUser instead? - domain = [[context activeUser] domain]; max = [records count]; for (count = 0; count < max; count++) @@ -1637,17 +1636,14 @@ static NSArray *childRecordFields = nil; currentUID = [record valueForKey: @"c_uid"]; if ([currentUID hasPrefix: @"@"]) { - group = [[SOGoCache sharedCache] groupNamed: currentUID inDomain: domain]; - - if (!group) - { - group = [SOGoGroup groupWithIdentifier: currentUID - inDomain: domain]; - [[SOGoCache sharedCache] registerGroup: group withName: currentUID inDomain: domain]; - } - - if (group && [group hasMemberWithUID: uid]) - [acls addObject: [record valueForKey: @"c_role"]]; + NSString *dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: currentUID]; + if (dict) + { + id source = [[SOGoUserManager sharedUserManager] sourceWithID: [dict objectForKey: @"SOGoSource"]]; + if ([source conformsToProtocol:@protocol(MembershipAwareSource)] && + [(id)(source) groupWithUIDHasMemberWithUID: currentUID memberUid: uid]) + [acls addObject: [record valueForKey: @"c_role"]]; + } } } @@ -1754,39 +1750,45 @@ static NSArray *childRecordFields = nil; forObjectAtPath: (NSArray *) objectPathArray { EOQualifier *qualifier; - NSString *uid, *uids, *qs, *objectPath, *domain; + NSString *uid, *uids, *qs, *objectPath; NSMutableArray *usersAndGroups, *groupsMembers; NSMutableDictionary *aclsForObject; - SOGoGroup *group; + unsigned int i; if ([users count] > 0) { - domain = [[context activeUser] domain]; usersAndGroups = [NSMutableArray arrayWithArray: users]; groupsMembers = [NSMutableArray array]; for (i = 0; i < [usersAndGroups count]; i++) { + NSDictionary *dict; + uid = [usersAndGroups objectAtIndex: i]; - group = [SOGoGroup groupWithIdentifier: uid inDomain: domain]; - if (group) + dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: uid]; + if (dict && [[dict objectForKey: @"isGroup"] boolValue]) { - NSArray *members; - SOGoUser *user; - unsigned int j; - - // Fetch members to remove them from the cache along the group - members = [group members]; - for (j = 0; j < [members count]; j++) + id source; + source = [[SOGoUserManager sharedUserManager] sourceWithID: [dict objectForKey: @"SOGoSource"]]; + if ([source conformsToProtocol:@protocol(MembershipAwareSource)]) { - user = [members objectAtIndex: j]; - [groupsMembers addObject: [user login]]; - } + NSArray *members; + SOGoUser *user; + unsigned int j; - if (![uid hasPrefix: @"@"]) - // Prefix the UID with the character "@" when dealing with a group - [usersAndGroups replaceObjectAtIndex: i - withObject: [NSString stringWithFormat: @"@%@", uid]]; + // Fetch members to remove them from the cache along the group + members = [(id)(source) membersForGroupWithUID: uid]; + for (j = 0; j < [members count]; j++) + { + user = [members objectAtIndex: j]; + [groupsMembers addObject: [user login]]; + } + + if (![uid hasPrefix: @"@"]) + // Prefix the UID with the character "@" when dealing with a group + [usersAndGroups replaceObjectAtIndex: i + withObject: [NSString stringWithFormat: @"@%@", uid]]; + } } } objectPath = [objectPathArray componentsJoinedByString: @"/"]; @@ -1846,9 +1848,8 @@ static NSArray *childRecordFields = nil; forUser: (NSString *) uid forObjectAtPath: (NSArray *) objectPathArray { - NSString *objectPath, *aUID, *domain; + NSString *objectPath, *aUID; NSMutableArray *newRoles; - SOGoGroup *group; objectPath = [objectPathArray componentsJoinedByString: @"/"]; @@ -1858,10 +1859,9 @@ static NSArray *childRecordFields = nil; aUID = uid; if (![uid hasPrefix: @"@"]) { - // Prefix the UID with the character "@" when dealing with a group - domain = [[context activeUser] domain]; - group = [SOGoGroup groupWithIdentifier: uid inDomain: domain]; - if (group) + NSDictionary *dict; + dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: uid]; + if ([[dict objectForKey: @"isGroup"] boolValue]) { aUID = [NSString stringWithFormat: @"@%@", uid]; // Remove all roles when defining ACLs for a group diff --git a/SoObjects/SOGo/SOGoGroup.h b/SoObjects/SOGo/SOGoGroup.h deleted file mode 100644 index bf6a8c61d..000000000 --- a/SoObjects/SOGo/SOGoGroup.h +++ /dev/null @@ -1,60 +0,0 @@ -/* SOGoGroup.h - this file is part of SOGo - * - * Copyright (C) 2009-2012 Inverse inc. - * - * Author: Ludovic Marcotte - * - * 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 __SOGOGROUP_H__ -#define __SOGOGROUP_H__ - -#import - -@class LDAPSource; -@class NSArray; -@class NSMutableArray; -@class NSString; -@class NGLdapEntry; - -@protocol SOGoSource; - -@interface SOGoGroup : NSObject -{ - @private - NSString *_identifier; - NSString *_domain; - NGLdapEntry *_entry; - NSObject *_source; - NSMutableArray *_members; -} - -+ (id) groupWithIdentifier: (NSString *) theID - inDomain: (NSString *) domain; -+ (id) groupWithEmail: (NSString *) theEmail - inDomain: (NSString *) domain; -+ (id) groupWithValue: (NSString *) theValue - andSourceSelector: (SEL) theSelector - inDomain: (NSString *) domain; - -- (NSArray *) members; - -- (BOOL) hasMemberWithUID: (NSString *) memberUID; - -@end - -#endif // __SOGOGROUP_H__ diff --git a/SoObjects/SOGo/SOGoGroup.m b/SoObjects/SOGo/SOGoGroup.m deleted file mode 100644 index 9de1aad53..000000000 --- a/SoObjects/SOGo/SOGoGroup.m +++ /dev/null @@ -1,370 +0,0 @@ -/* SOGoGroup.m - this file is part of SOGo - * - * Copyright (C) 2009-2014 Inverse inc. - * - * 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. - */ - -/* - Here are some group samples: - - [ POSIX group ] - - dn: cn=it-staff,ou=Group,dc=zzz,dc=xxx,dc=yyy - objectClass: posixGroup - objectClass: top - cn: it-staff - userPassword: {crypt}x - gidNumber: 8000 - memberUid: lsa - memberUid: mrm - memberUid: ij - memberUid: no - memberUid: ld - memberUid: db - memberUid: rgl - memberUid: ja - memberUid: hbt - memberUid: hossein - - dn: cn=inverse,ou=groups,dc=inverse,dc=ca - objectClass: groupOfUniqueNames - objectClass: top - objectClass: extensibleObject - uniqueMember: uid=flachapelle,ou=users,dc=inverse,dc=ca - uniqueMember: uid=lmarcotte,ou=users,dc=inverse,dc=ca - uniqueMember: uid=wsourdeau,ou=users,dc=inverse,dc=ca - cn: inverse - mail: inverse@inverse.ca - - */ - -#include "SOGoGroup.h" - - -#include "SOGoCache.h" -#include "SOGoSource.h" -#include "SOGoSystemDefaults.h" -#include "SOGoUserManager.h" -#include "SOGoUser.h" - -#import - -#define CHECK_CLASS(o) ({ \ - if ([o isKindOfClass: [NSString class]]) \ - o = [NSArray arrayWithObject: o]; \ -}) - -@implementation SOGoGroup - -- (id) initWithIdentifier: (NSString *) theID - domain: (NSString *) theDomain - source: (NSObject *) theSource - entry: (NGLdapEntry *) theEntry -{ - self = [super init]; - - if (self) - { - ASSIGN(_identifier, theID); - ASSIGN(_domain, theDomain); - ASSIGN(_source, theSource); - ASSIGN(_entry, theEntry); - _members = nil; - } - - return self; -} - -- (void) dealloc -{ - RELEASE(_identifier); - RELEASE(_domain); - RELEASE(_source); - RELEASE(_entry); - RELEASE(_members); - - [super dealloc]; -} - -+ (id) groupWithIdentifier: (NSString *) theID - inDomain: (NSString *) domain -{ - NSRange r; - NSString *uid, *inDomain; - SOGoSystemDefaults *sd; - - uid = [theID hasPrefix: @"@"] ? [theID substringFromIndex: 1] : theID; - inDomain = domain; - - sd = [SOGoSystemDefaults sharedSystemDefaults]; - if ([sd enableDomainBasedUID]) - { - /* Split domain from uid */ - r = [uid rangeOfString: @"@" options: NSBackwardsSearch]; - if (r.location != NSNotFound) - { - if (!domain) - inDomain = [uid substringFromIndex: r.location + 1]; - uid = [uid substringToIndex: r.location]; - } - } - - return [SOGoGroup groupWithValue: uid - andSourceSelector: @selector (lookupGroupEntryByUID:inDomain:) - inDomain: inDomain]; -} - -+ (id) groupWithEmail: (NSString *) theEmail - inDomain: (NSString *) domain -{ - return [SOGoGroup groupWithValue: theEmail - andSourceSelector: @selector (lookupGroupEntryByEmail:inDomain:) - inDomain: domain]; -} - -// -// Returns nil if theValue doesn't match to a group -// (so its objectClass isn't a group) -// -+ (id) groupWithValue: (NSString *) theValue - andSourceSelector: (SEL) theSelector - inDomain: (NSString *) domain -{ - NSArray *allSources; - NGLdapEntry *entry; - NSObject *source; - id o; - NSEnumerator *gclasses; - NSString *gclass; - - int i; - - // Don't bother looking in all sources if the - // supplied value is nil. - if (!theValue) - return nil; - - allSources = [[SOGoUserManager sharedUserManager] - sourceIDsInDomain: domain]; - entry = nil; - o = nil; - - for (i = 0; i < [allSources count]; i++) - { - source = (NSObject *) [[SOGoUserManager sharedUserManager] sourceWithID: [allSources objectAtIndex: i]]; - - // Our different sources might not all implements groups support - if ([source respondsToSelector: theSelector]) - entry = [source performSelector: theSelector - withObject: theValue - withObject: domain]; - if (entry) - break; - - entry = nil; - } - - if (entry) - { - NSArray *classes; - - // We check to see if it's a group - classes = [[entry asDictionary] objectForKey: @"objectclass"]; - - if (classes) - { - /* LDAP records returned as dictionaries may contain NSString or - NSArray values, depending on whether the amount of values - assigned to a key is 1 or more. Since this can occur with - "objectclass" too, we need to check whether "classes" is actually - an NSString instance... */ - if ([classes isKindOfClass: [NSString class]]) - classes = [NSArray arrayWithObject: - [(NSString *) classes lowercaseString]]; - else - { - int i, c; - - classes = [NSMutableArray arrayWithArray: classes]; - c = [classes count]; - for (i = 0; i < c; i++) - [(id)classes replaceObjectAtIndex: i - withObject: [[classes objectAtIndex: i] lowercaseString]]; - } - } - - gclasses = [[source groupObjectClasses] objectEnumerator]; - while ((gclass = [gclasses nextObject])) - if ([classes containsObject: gclass]) - { - // Found a group, let's return it. - o = [[self alloc] initWithIdentifier: theValue - domain: domain - source: source - entry: entry]; - AUTORELEASE(o); - } - } - - return o; -} - -// -// This method actually try to obtain all members -// from either dynamic of static groups. -// -- (NSArray *) members -{ - NSMutableArray *dns, *uids, *logins; - NSString *dn, *login; - SOGoUserManager *um; - NSDictionary *d; - SOGoUser *user; - NSArray *o; - NSAutoreleasePool *pool; - int i, c; - - if (!_members) - { - _members = [NSMutableArray new]; - uids = [NSMutableArray array]; - dns = [NSMutableArray array]; - logins = [NSMutableArray array]; - - // We check if it's a static group - // Fetch "members" - we get DNs - d = [_entry asDictionary]; - o = [d objectForKey: @"member"]; - CHECK_CLASS(o); - if (o) [dns addObjectsFromArray: o]; - - // Fetch "uniqueMembers" - we get DNs - o = [d objectForKey: @"uniquemember"]; - CHECK_CLASS(o); - if (o) [dns addObjectsFromArray: o]; - - // Fetch "memberUid" - we get UID (like login names) - o = [d objectForKey: @"memberuid"]; - CHECK_CLASS(o); - if (o) [uids addObjectsFromArray: o]; - - c = [dns count] + [uids count]; - - // We deal with a static group, let's add the members - if (c) - { - um = [SOGoUserManager sharedUserManager]; - - // We add members for whom we have their associated DN - for (i = 0; i < [dns count]; i++) - { - pool = [NSAutoreleasePool new]; - dn = [dns objectAtIndex: i]; - login = [um getLoginForDN: [dn lowercaseString]]; - user = [SOGoUser userWithLogin: login roles: nil]; - if (user) - { - [logins addObject: login]; - [_members addObject: user]; - } - [pool release]; - } - - // We add members for whom we have their associated login name - for (i = 0; i < [uids count]; i++) - { - pool = [NSAutoreleasePool new]; - login = [uids objectAtIndex: i]; - user = [SOGoUser userWithLogin: login roles: nil]; - - if (user) - { - [logins addObject: [user loginInDomain]]; - [_members addObject: user]; - } - [pool release]; - } - - - // We are done fetching members, let's cache the members of the group - // (ie., their UIDs) in memcached to speed up -hasMemberWithUID. - [[SOGoCache sharedCache] setValue: [logins componentsJoinedByString: @","] - forKey: [NSString stringWithFormat: @"%@+%@", _identifier, _domain]]; - } - else - { - // We deal with a dynamic group, let's search all users for whom - // memberOf is equal to our group's DN. - // We also need to look for labelelURI? - } - } - - return _members; -} - -// -// -// -- (BOOL) hasMemberWithUID: (NSString *) memberUID -{ - - BOOL rc; - - rc = NO; - - // If _members is initialized, we use it as it's very accurate. - // Otherwise, we fallback on memcached in order to avoid - // decomposing the group all the time just to see if a user - // is a member of it. - if (_members) - { - NSString *currentUID; - - int count, max; - max = [_members count]; - for (count = 0; !rc && count < max; count++) - { - currentUID = [[_members objectAtIndex: count] login]; - rc = [memberUID isEqualToString: currentUID]; - } - - } - else - { - NSString *key, *value;; - NSArray *a; - - key = [NSString stringWithFormat: @"%@+%@", _identifier, _domain]; - value = [[SOGoCache sharedCache] valueForKey: key]; - - - // If the value isn't in memcached, that probably means -members was never called. - // We call it only once here. - if (!value) - { - [self members]; - value = [[SOGoCache sharedCache] valueForKey: key]; - } - - a = [value componentsSeparatedByString: @","]; - rc = [a containsObject: memberUID]; - } - - return rc; -} - -@end diff --git a/SoObjects/SOGo/SOGoSource.h b/SoObjects/SOGo/SOGoSource.h index 4644eab3f..a9f1bddbe 100644 --- a/SoObjects/SOGo/SOGoSource.h +++ b/SoObjects/SOGo/SOGoSource.h @@ -118,4 +118,10 @@ - (void) updateBaseDNFromLogin: (NSString *) theLogin; @end +@protocol MembershipAwareSource +- (NSArray *) membersForGroupWithUID: (NSString *) uid; +- (BOOL) groupWithUIDHasMemberWithUID: (NSString *) uid + memberUid: (NSString *) memberUid; +@end + #endif /* SOGOSOURCE_H */ diff --git a/UI/Common/UIxUserRightsEditor.m b/UI/Common/UIxUserRightsEditor.m index a9da6726e..941a2be88 100644 --- a/UI/Common/UIxUserRightsEditor.m +++ b/UI/Common/UIxUserRightsEditor.m @@ -27,7 +27,6 @@ #import #import #import -#import #import #import @@ -120,10 +119,10 @@ - (BOOL) _initRights { BOOL response; - NSString *newUID, *domain; + NSString *newUID; SOGoUserManager *um; SOGoObject *clientObject; - SOGoGroup *group; + NSDictionary *dict; response = NO; @@ -140,9 +139,8 @@ { if (![newUID hasPrefix: @"@"]) { - domain = [[context activeUser] domain]; - group = [SOGoGroup groupWithIdentifier: newUID inDomain: domain]; - if (group) + dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: newUID]; + if (dict && [[dict objectForKey: @"isGroup"] boolValue]) newUID = [NSString stringWithFormat: @"@%@", newUID]; } ASSIGN (uid, newUID); @@ -158,10 +156,9 @@ - (BOOL) _initRightsForUserID:(NSString *) newUID { BOOL response; - NSString *domain; SOGoUserManager *um; SOGoObject *clientObject; - SOGoGroup *group; + NSDictionary *dict; response = NO; @@ -174,11 +171,11 @@ if ([newUID isEqualToString: defaultUserID] || [newUID isEqualToString: @"anonymous"] || [[um getEmailForUID: newUID] length] > 0) { +#warning Duplicated code from _initRights if (![newUID hasPrefix: @"@"]) { - domain = [[context activeUser] domain]; - group = [SOGoGroup groupWithIdentifier: newUID inDomain: domain]; - if (group) + dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: newUID]; + if (dict && [[dict objectForKey: @"isGroup"] boolValue]) newUID = [NSString stringWithFormat: @"@%@", newUID]; } ASSIGN (uid, newUID); diff --git a/UI/Contacts/UIxContactView.m b/UI/Contacts/UIxContactView.m index 73f22c399..7f333988e 100644 --- a/UI/Contacts/UIxContactView.m +++ b/UI/Contacts/UIxContactView.m @@ -31,8 +31,6 @@ #import #import #import -#import -#import #import #import #import @@ -409,13 +407,9 @@ if ([[dict objectForKey: @"isGroup"] boolValue]) { - if ([source isKindOfClass: [LDAPSource class]] && [(LDAPSource *) source groupExpansionEnabled]) + if ([source conformsToProtocol:@protocol(MembershipAwareSource)]) { - SOGoGroup *aGroup; - - aGroup = [SOGoGroup groupWithIdentifier: [contact nameInContainer] - inDomain: [[context activeUser] domain]]; - allUsers = [aGroup members]; // array of SOGoUser objects + allUsers = [(id)(source) membersForGroupWithUID: [dict objectForKey: @"c_uid"]]; max = [allUsers count]; allUsersData = [NSMutableArray arrayWithCapacity: max]; for (i = 0; i < max; i++)