See ChangeLog

Monotone-Parent: 6daecf153c7b456921285f46297f307b36b5ceb7
Monotone-Revision: 4cbfa058c2199806b08f5a11ac5cebf02316041f

Monotone-Author: ludovic@Sophos.ca
Monotone-Date: 2009-04-30T21:17:55
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Ludovic Marcotte 2009-04-30 21:17:55 +00:00
parent 9f9696fd32
commit e53507146c
12 changed files with 528 additions and 14 deletions

View File

@ -1,3 +1,10 @@
2009-04-30 Ludovic Marcotte <lmarcotte@inverse.ca>
* Added SoObjects/SOGo/SOGoGroup.{h.m} and
modified the LDAPSource, LDAPUserManager, SOGoAppoinmentObject
in order to add initial groups support which decomposes
groups to members when events are saved.
2009-04-27 Francis Lachapelle <flachapelle@inverse.ca>
* UI/Scheduler/UIxCalListingActions.m ([UIxCalListingActions

View File

@ -54,12 +54,10 @@
#import <NGExtensions/NSObject+Logs.h>
#import <SaxObjC/XMLNamespaces.h>
// #import <NGObjWeb/SoClassSecurityInfo.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSObject+DAV.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoCache.h>
// #import <SOGo/SOGoCustomGroupFolder.h>
#import <SOGo/LDAPUserManager.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/SOGoPermissions.h>

View File

@ -1,15 +1,15 @@
/*
Copyright (C) 2004-2005 SKYRIX Software AG
Copyright (C) 2007-2008 Inverse inc.
Copyright (C) 2007-2009 Inverse inc.
This file is part of OpenGroupware.org.
OGo is free software; you can redistribute it and/or modify it under
SOGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
SOGo 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 Lesser General Public
License for more details.
@ -40,6 +40,7 @@
#import <SoObjects/SOGo/NSObject+DAV.h>
#import <SoObjects/SOGo/SOGoObject.h>
#import <SoObjects/SOGo/SOGoPermissions.h>
#import <SoObjects/SOGo/SOGoGroup.h>
#import <SoObjects/SOGo/SOGoUser.h>
#import <SoObjects/SOGo/SOGoWebDAVAclManager.h>
#import <SoObjects/SOGo/SOGoWebDAVValue.h>
@ -395,24 +396,92 @@
toAttendees: updateAttendees];
}
- (void) _handleAddedUsers: (NSArray *) attendees
//
// Returns "YES" if a group was decomposed during attendees addition.
//
- (BOOL) _handleAddedUsers: (NSArray *) attendees
fromEvent: (iCalEvent *) newEvent
{
NSMutableArray *array, *attendeesFromGroups;
NSEnumerator *enumerator;
iCalPerson *currentAttendee;
NSString *currentUID;
BOOL b;
int i;
array = [NSMutableArray arrayWithArray: [newEvent attendees]];
attendeesFromGroups = [NSMutableArray array];
RETAIN(attendees);
b = NO;
enumerator = [attendees objectEnumerator];
while ((currentAttendee = [enumerator nextObject]))
{
currentUID = [currentAttendee uid];
if (currentUID)
SOGoGroup *group;
group = [SOGoGroup groupWithIdentifier: [currentAttendee rfc822Email]];
if (group)
{
iCalPerson *person;
NSArray *members;
SOGoUser *user;
// We did decompose a group...
[array removeObject: currentAttendee];
b = YES;
members = [group members];
for (i = 0; i < [members count]; i++)
{
user = [members objectAtIndex: i];
// If the organizer is part of the group, we skip it from
// the addition to the attendees' list
if ([user hasEmail: [[newEvent organizer] rfc822Email]])
continue;
person = [self iCalPersonWithUID: [user login]];
[person setTag: @"ATTENDEE"];
[person setParticipationStatus: iCalPersonPartStatNeedsAction];
[person setRsvp: @"TRUE"];
[person setRole: @"REQ-PARTICIPANT"];
[attendeesFromGroups addObject: [user login]];
if (![array containsObject: person])
[array addObject: person];
}
}
else
{
currentUID = [currentAttendee uid];
if (currentUID)
[self _addOrUpdateEvent: newEvent
forUID: currentUID
owner: owner];
}
}
if (b)
{
NSLog(@"New attendees: %@", array);
[newEvent setAttendees: array];
for (i = 0; i < [attendeesFromGroups count]; i++)
[self _addOrUpdateEvent: newEvent
forUID: currentUID
forUID: [attendeesFromGroups objectAtIndex: i]
owner: owner];
}
RELEASE(attendees);
return b;
}
//
//
//
- (void) _handleUpdatedEvent: (iCalEvent *) newEvent
fromOldEvent: (iCalEvent *) oldEvent
{
@ -467,8 +536,19 @@
if ([attendees count])
{
NSArray *originalAttendees;
originalAttendees = [NSArray arrayWithArray: [newEvent attendees]];
// Send an invitation to new attendees
[self _handleAddedUsers: attendees fromEvent: newEvent];
if ([self _handleAddedUsers: attendees fromEvent: newEvent])
{
// We need to compute our new set for the invitation template
// if we decomposed groups.
attendees = [NSMutableArray arrayWithArray: [newEvent attendees]];
[(NSMutableArray *)attendees removeObjectsInArray: originalAttendees];
}
[self sendEMailUsingTemplateNamed: @"Invitation"
forObject: [newEvent itipEntryWithMethod: @"request"]
previousObject: oldEvent
@ -499,7 +579,13 @@
attendees = [newEvent attendeesWithoutUser: ownerUser];
if ([attendees count])
{
[self _handleAddedUsers: attendees fromEvent: newEvent];
if ([self _handleAddedUsers: attendees fromEvent: newEvent])
{
// We refetch the list of attendees and save again the
// event as a group was decomposed
attendees = [newEvent attendeesWithoutUser: ownerUser];
[super saveComponent: newEvent];
}
[self sendEMailUsingTemplateNamed: @"Invitation"
forObject: [newEvent itipEntryWithMethod: @"request"]
previousObject: nil
@ -529,7 +615,8 @@
}
[self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent];
// The sequence has possibly been increased -- resave the event
// The sequence has possibly been increased -- resave the event.
// This will also take care of a decomposed group.
[super saveComponent: newEvent];
}
}
@ -1224,4 +1311,84 @@
return @"IPM.Appointment";
}
//
// If we see "X-SOGo: NoGroupsDecomposition" in the HTTP headers, we
// simply invoke super's PUTAction.
//
- (id) PUTAction: (WOContext *) _ctx
{
iCalPerson *currentAttendee;
NSEnumerator *enumerator;
iCalCalendar *calendar;
NSMutableArray *array;
NSArray *allEvents;
SOGoGroup *group;
iCalEvent *event;
WORequest *rq;
int i;
rq = [_ctx request];
//NSLog(@"Content from request: %@", [rq contentAsString]);
// The algorithm is pretty straightforward:
//
// We get all events
// We get all attendees
// If some are groups, we decompose them
// We regenerate the iCalendar string
//
calendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]];
allEvents = [calendar events];
for (i = 0; i < [allEvents count]; i++)
{
event = [allEvents objectAtIndex: i];
array = [NSMutableArray arrayWithArray: [event attendees]];
enumerator = [[event attendees] objectEnumerator];
while ((currentAttendee = [enumerator nextObject]))
{
group = [SOGoGroup groupWithIdentifier: [currentAttendee rfc822Email]];
if (group)
{
iCalPerson *person;
NSArray *members;
SOGoUser *user;
// We did decompose a group...
[array removeObject: currentAttendee];
members = [group members];
for (i = 0; i < [members count]; i++)
{
user = [members objectAtIndex: i];
// If the organizer is part of the group, we skip it from
// the addition to the attendees' list
if ([user hasEmail: [[event organizer] rfc822Email]])
continue;
person = [self iCalPersonWithUID: [user login]];
[person setTag: @"ATTENDEE"];
[person setParticipationStatus: iCalPersonPartStatNeedsAction];
[person setRsvp: @"TRUE"];
[person setRole: @"REQ-PARTICIPANT"];
if (![array containsObject: person])
[array addObject: person];
}
}
}
[event setAttendees: array];
}
//NSLog(@"Content from calendar:secure: %@", [calendar versitString]);
[rq setContent: [[calendar versitString] dataUsingEncoding: [rq contentEncoding]]];
return [super PUTAction: _ctx];
}
@end /* SOGoAppointmentObject */

View File

@ -1,6 +1,6 @@
/* SOGoCalendarComponent.m - this file is part of SOGo
*
* Copyright (C) 2006-2008 Inverse inc.
* Copyright (C) 2006-2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*

View File

@ -49,6 +49,7 @@ SOGo_HEADER_FILES = \
SOGoWebDAVAclManager.h \
SOGoWebDAVValue.h \
SOGoMailer.h \
SOGoGroup.h \
SOGoUser.h \
\
NSDictionary+BSJSONAdditions.h \
@ -94,6 +95,7 @@ SOGo_OBJC_FILES = \
SOGoWebDAVAclManager.m \
SOGoWebDAVValue.m \
SOGoMailer.m \
SOGoGroup.m \
SOGoUser.m \
\
NSDictionary+BSJSONAdditions.m \

View File

@ -28,6 +28,7 @@
@class NSDictionary;
@class NSString;
@class NGLdapConnection;
@class NGLdapEntry;
@interface LDAPSource : NSObject
{
@ -62,6 +63,7 @@
hostname: (NSString *) newBindHostname
port: (NSString *) newBindPort
encryption: (NSString *) newEncryption;
- (void) setBaseDN: (NSString *) newBaseDN
IDField: (NSString *) newIDField
CNField: (NSString *) newCNField
@ -69,11 +71,15 @@
mailFields: (NSArray *) newMailFields
andBindFields: (NSString *) newBindFields;
- (NSString *) loginForDN: (NSString *) theDN;
- (BOOL) checkLogin: (NSString *) login
andPassword: (NSString *) password;
- (NSDictionary *) lookupContactEntry: (NSString *) theID;
- (NSDictionary *) lookupContactEntryWithUIDorEmail: (NSString *) entryID;
- (NGLdapEntry *) lookupGroupEntry: (NSString *) theID;
- (NSArray *) allEntryIDs;
- (NSArray *) fetchContactsMatching: (NSString *) filter;
- (NSString *) sourceID;

View File

@ -377,6 +377,21 @@ static NSLock *lock;
return userDN;
}
- (NSString *) loginForDN: (NSString *) theDN
{
NGLdapEntry *entry;
entry = [ldapConnection entryAtDN: theDN
attributes: [NSArray arrayWithObject: IDField]];
if (entry)
{
return [[entry attributeWithName: IDField] stringValueAtIndex: 0];
}
return nil;
}
- (BOOL) checkLogin: (NSString *) loginToCheck
andPassword: (NSString *) passwordToCheck
{
@ -822,6 +837,61 @@ static NSLock *lock;
return contactEntry;
}
// Use the email address for now...
- (NGLdapEntry *) lookupGroupEntry: (NSString *) theID
{
NGLdapEntry *ldapEntry;
#if defined(THREADSAFE)
[lock lock];
#endif
ldapEntry = nil;
if ([theID length] > 0)
{
if ([self _initLDAPConnection])
{
NSMutableArray *attributes;
NSEnumerator *entries;
EOQualifier *qualifier;
NSString *s;
// FIXME
s = [NSString stringWithFormat: @"(mail='%@')", theID];
qualifier = [EOQualifier qualifierWithQualifierFormat: s];
// We look for additional attributes - the ones related to group membership
attributes = [NSMutableArray arrayWithArray: [self _searchAttributes]];
[attributes addObject: @"member"];
[attributes addObject: @"memberOf"];
if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame)
entries = [ldapConnection baseSearchAtBaseDN: baseDN
qualifier: qualifier
attributes: attributes];
else if ([_scope caseInsensitiveCompare: @"ONE"] == NSOrderedSame)
entries = [ldapConnection flatSearchAtBaseDN: baseDN
qualifier: qualifier
attributes: attributes];
else
entries = [ldapConnection deepSearchAtBaseDN: baseDN
qualifier: qualifier
attributes: attributes];
ldapEntry = [entries nextObject];
}
[ldapConnection autorelease];
}
#if defined(THREADSAFE)
[lock unlock];
#endif
return ldapEntry;
}
- (NSString *) sourceID
{
return sourceID;

View File

@ -29,6 +29,7 @@
@class NSMutableDictionary;
@class NSString;
@class NSTimer;
@class NGLdapEntry;
@class LDAPSource;

View File

@ -164,6 +164,7 @@ static NSLock *lock = nil;
- (void) dealloc
{
[sources release];
[sourcesMetadata release];
[super dealloc];
}

View File

@ -1,6 +1,6 @@
/* SOGoFolder.m - this file is part of SOGo
*
* Copyright (C) 2007 Inverse inc.
* Copyright (C) 2007-2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*

View File

@ -0,0 +1,47 @@
/* SOGoGroup.h - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
*
* Author: Ludovic Marcotte <lmarcotte@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 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 <Foundation/NSObject.h>
@class LDAPSource;
@class NSArray;
@class NSString;
@class NGLdapEntry;
@interface SOGoGroup : NSObject
{
@private
NSString *_identifier;
NGLdapEntry *_entry;
LDAPSource *_source;
}
+ (id) groupWithIdentifier: (NSString *) theID;
- (NSArray *) members;
@end
#endif // __SOGOGROUP_H__

View File

@ -0,0 +1,215 @@
/* SOGoGroup.m - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
*
* Author: Ludovic Marcotte <lmarcotte@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 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
*/
#include "SOGoGroup.h"
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
#include "LDAPSource.h"
#include "LDAPUserManager.h"
#include "SOGoUser.h"
#import <NGLdap/NGLdapConnection.h>
#import <NGLdap/NGLdapAttribute.h>
#import <NGLdap/NGLdapEntry.h>
@implementation SOGoGroup
- (id) initWithIdentifier: (NSString *) theID
source: (LDAPSource *) theSource
entry: (NGLdapEntry *) theEntry
{
self = [super init];
if (self)
{
ASSIGN(_identifier, theID);
ASSIGN(_source, theSource);
ASSIGN(_entry, theEntry);
}
return self;
}
- (void) dealloc
{
RELEASE(_identifier);
RELEASE(_source);
RELEASE(_entry);
[super dealloc];
}
//
// Returns nil if theID (which is an email address) doesn't
// actually match to a group (so its objectClass isn't really a group)
//
+ (id) groupWithIdentifier: (NSString *) theID
{
NSArray *allSources;
NGLdapEntry *entry;
LDAPSource *source;
id o;
int i;
// Don't bother looking in all sources if the
// supplied email address is nil.
if (!theID)
return nil;
allSources = [[LDAPUserManager sharedUserManager] sourceIDs];
o = nil;
for (i = 0; i < [allSources count]; i++)
{
source = [[LDAPUserManager sharedUserManager] sourceWithID: [allSources objectAtIndex: i]];
entry = [source lookupGroupEntry: theID];
if (entry)
break;
entry = nil;
}
if (entry)
{
NSArray *classes;
// We check to see if it's a group
classes = [[entry attributeWithName: @"objectClass"] allStringValues];
NSLog(@"classes = %@", classes);
// Found a group, let's return it.
if ([classes containsObject: @"group"] ||
[classes containsObject: @"groupOfNames"] ||
[classes containsObject: @"groupOfUniqueNames"] ||
[classes containsObject: @"posixGroup"])
{
o = [[self alloc] initWithIdentifier: theID
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;
NSMutableArray *array;
NSString *login;
SOGoUser *user;
NSArray *o;
int i, c;
array = [NSMutableArray array];
uids = [NSMutableArray array];
dns = [NSMutableArray array];
// We check if it's a static group
NSLog(@"attributes = %@", [_entry attributes]);
// Fetch "members" - we get DNs
o = [[_entry attributeWithName: @"member"] allStringValues];
if (o) [dns addObjectsFromArray: o];
// Fetch "uniqueMembers" - we get DNs
o = [[_entry attributeWithName: @"uniqueMember"] allStringValues];
if (o) [dns addObjectsFromArray: o];
// Fetch "memberUid" - we get UID (like login names)
o = [[_entry attributeWithName: @"memberUid"] allStringValues];
if (o) [uids addObjectsFromArray: o];
c = [dns count] + [uids count];
NSLog(@"members count (static group): %d", c);
// We deal with a static group, let's add the members
if (c)
{
// We add members for whom we have their associated DN
for (i = 0; i < [dns count]; i++)
{
login = [_source loginForDN: [dns objectAtIndex: i]];
NSLog(@"member = %@", login);
user = [SOGoUser userWithLogin: login roles: nil];
if (user)
[array addObject: user];
}
// We add members for whom we have their associated login name
for (i = 0; i < [uids count]; i++)
{
login = [uids objectAtIndex: i];
NSLog(@"member = %@", login);
user = [SOGoUser userWithLogin: login roles: nil];
if (user)
[array addObject: user];
}
}
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 array;
}
@end