Monotone-Parent: a8698cc119066cef660b176c3e956b5d0d843e7f
Monotone-Revision: f5b8996b9296ee950ea712c8de2c37e474163519 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2009-08-27T21:14:54 Monotone-Branch: ca.inverse.sogomaint-2.0.2
parent
a38722d9cb
commit
06b296ddba
42
ChangeLog
42
ChangeLog
|
@ -1,5 +1,47 @@
|
|||
2009-08-27 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/Appointments/SOGoCalendarProxy.[hm]: new class module
|
||||
implementing the caldav proxy collections mentionned below. This
|
||||
is a subclass of SOGoFolder.
|
||||
|
||||
* SoObjects/SOGo/WORequest+SOGo.m (-isAppleDAVWithSubstring:): new
|
||||
method.
|
||||
(-isICal): new method.
|
||||
|
||||
* SoObjects/SOGo/SOGoUserFolder.m
|
||||
(-calendarProxy:withWriteAccess:): new method that instantiate a
|
||||
proxy collections for read and write delegations.
|
||||
|
||||
* SoObjects/SOGo/SOGoParentFolder.m (-initSubscribedFolders): we
|
||||
now make use of appendSubcribedSources with which we shared code.
|
||||
|
||||
* SoObjects/SOGo/SOGoGCSFolder.m (-subscribeUser:reallyDo:): new
|
||||
simplified and published version of
|
||||
subscribe:inTheNamesOf:fromMailInvitation:inContext:.
|
||||
|
||||
* SoObjects/SOGo/SOGoFolder.m (-davGroupMemberSet)
|
||||
(davGroupMembership): new DAV accessors that return an empty
|
||||
array.
|
||||
|
||||
* SoObjects/Appointments/SOGoUserFolder+Appointments.m
|
||||
(-davGroupMembership): new DAV accessor.
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentFolders.m
|
||||
(-toManyRelationshipKeys): overriden method for iCal: we don't
|
||||
list the folders to which the current user is subscribed to
|
||||
(because the iCal paradigm now requires calendar proxying), we
|
||||
don't list the folders to which a delegator has subscribed to
|
||||
either nor any of his/her secondary calendars.
|
||||
(-proxyFoldersWithWriteAccess:): new methods that returns the list
|
||||
of personal folder on which we are allowed to act as a proxy.
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
(-requiredProxyRolesWithWriteAccess:): new method that returnes
|
||||
the roles required for read/write proxying of users.
|
||||
(-proxySubscribersWithWriteAccess:)
|
||||
(setProxySubscribers:withWriteAccess:): new accessors making use
|
||||
of the new method above.
|
||||
|
||||
* Tests/test-caldav-scheduling.py: new set of tests for CalDAV
|
||||
scheduling (iTIP-over-DAV) operations. Implemented 9 scenarios for
|
||||
invitation delegation.
|
||||
|
|
1
NEWS
1
NEWS
|
@ -8,6 +8,7 @@
|
|||
- a context menu is now available for tasks
|
||||
- added the capability of creating and managing lists of contacts (same as in Thunderbird)
|
||||
- added support for short date format in the calendar views
|
||||
- added support for iCal delegation
|
||||
|
||||
1.0-20090812 (1.0.4)
|
||||
--------------------
|
||||
|
|
|
@ -28,6 +28,8 @@ Appointments_OBJC_FILES = \
|
|||
SOGoFreeBusyObject.m \
|
||||
SOGoUserFolder+Appointments.m \
|
||||
\
|
||||
SOGoCalendarProxy.m \
|
||||
\
|
||||
SOGoAptMailNotification.m \
|
||||
SOGoAptMailInvitation.m \
|
||||
SOGoAptMailDeletion.m \
|
||||
|
|
|
@ -140,6 +140,10 @@
|
|||
- (BOOL) showCalendarTasks;
|
||||
- (void) setShowCalendarTasks: (BOOL) new;
|
||||
|
||||
- (NSArray *) proxySubscribersWithWriteAccess: (BOOL) hasWriteAccess;
|
||||
- (NSException *) setProxySubscribers: (NSArray *) newSubscribers
|
||||
withWriteAccess: (BOOL) hasWriteAccess;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __Appointments_SOGoAppointmentFolder_H__ */
|
||||
|
|
|
@ -3055,4 +3055,122 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
|||
return (![inactiveFolders containsObject: nameInContainer]);
|
||||
}
|
||||
|
||||
- (NSArray *) requiredProxyRolesWithWriteAccess: (BOOL) hasWriteAccess
|
||||
{
|
||||
static NSArray *writeAccessRoles = nil;
|
||||
static NSArray *readAccessRoles = nil;
|
||||
|
||||
if (!writeAccessRoles)
|
||||
{
|
||||
writeAccessRoles = [NSArray arrayWithObjects:
|
||||
SOGoCalendarRole_ConfidentialModifier,
|
||||
SOGoRole_ObjectCreator,
|
||||
SOGoRole_ObjectEraser,
|
||||
SOGoCalendarRole_PrivateModifier,
|
||||
SOGoCalendarRole_PublicModifier,
|
||||
nil];
|
||||
[writeAccessRoles retain];
|
||||
}
|
||||
|
||||
if (!readAccessRoles)
|
||||
{
|
||||
readAccessRoles = [NSArray arrayWithObjects:
|
||||
SOGoCalendarRole_ConfidentialViewer,
|
||||
SOGoCalendarRole_PrivateViewer,
|
||||
SOGoCalendarRole_PublicViewer,
|
||||
nil];
|
||||
[readAccessRoles retain];
|
||||
}
|
||||
|
||||
return (hasWriteAccess) ? writeAccessRoles : readAccessRoles;
|
||||
}
|
||||
|
||||
- (BOOL) _user: (NSString *) user
|
||||
isProxyWithWriteAccess: (BOOL) hasWriteAccess
|
||||
{
|
||||
NSArray *userRoles, *reqRoles;
|
||||
BOOL isProxy;
|
||||
|
||||
if ([self userIsSubscriber: user])
|
||||
{
|
||||
userRoles = [[self aclsForUser: user]
|
||||
sortedArrayUsingSelector: @selector (compare:)];
|
||||
reqRoles = [self requiredProxyRolesWithWriteAccess: hasWriteAccess];
|
||||
isProxy = [reqRoles isEqualToArray: userRoles];
|
||||
}
|
||||
else
|
||||
isProxy = NO;
|
||||
|
||||
return isProxy;
|
||||
}
|
||||
|
||||
- (NSArray *) proxySubscribersWithWriteAccess: (BOOL) hasWriteAccess
|
||||
{
|
||||
NSMutableArray *subscribers;
|
||||
NSEnumerator *aclUsers;
|
||||
NSString *currentUser, *defaultUser;
|
||||
|
||||
subscribers = [NSMutableArray array];
|
||||
|
||||
defaultUser = [self defaultUserID];
|
||||
aclUsers = [[self aclUsers] objectEnumerator];
|
||||
while ((currentUser = [aclUsers nextObject]))
|
||||
{
|
||||
if (![currentUser isEqualToString: defaultUser]
|
||||
&& [self _user: currentUser
|
||||
isProxyWithWriteAccess: hasWriteAccess])
|
||||
[subscribers addObject: currentUser];
|
||||
}
|
||||
|
||||
return subscribers;
|
||||
}
|
||||
|
||||
- (NSException *) setProxySubscribers: (NSArray *) newSubscribers
|
||||
withWriteAccess: (BOOL) hasWriteAccess
|
||||
{
|
||||
int count, max;
|
||||
NSArray *oldSubscribers;
|
||||
NSString *login, *reason;
|
||||
NSException *error;
|
||||
|
||||
error = nil;
|
||||
|
||||
max = [newSubscribers count];
|
||||
for (count = 0; !error && count < max; count++)
|
||||
{
|
||||
login = [newSubscribers objectAtIndex: count];
|
||||
if (![SOGoUser userWithLogin: login roles: nil])
|
||||
{
|
||||
reason = [NSString stringWithFormat: @"User '%@' does not exist.", login];
|
||||
error = [NSException exceptionWithHTTPStatus: 403 reason: reason];
|
||||
}
|
||||
}
|
||||
|
||||
if (!error)
|
||||
{
|
||||
oldSubscribers = [self proxySubscribersWithWriteAccess: hasWriteAccess];
|
||||
for (count = 0; !error && count < max; count++)
|
||||
{
|
||||
login = [newSubscribers objectAtIndex: count];
|
||||
[self
|
||||
setRoles: [self requiredProxyRolesWithWriteAccess: hasWriteAccess]
|
||||
forUser: login];
|
||||
[self subscribeUser: login reallyDo: YES];
|
||||
}
|
||||
|
||||
max = [oldSubscribers count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
login = [oldSubscribers objectAtIndex: count];
|
||||
if (![newSubscribers containsObject: login])
|
||||
{
|
||||
[self subscribeUser: login reallyDo: NO];
|
||||
[self removeAclsForUsers: [NSArray arrayWithObject: login]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@end /* SOGoAppointmentFolder */
|
||||
|
|
|
@ -25,8 +25,12 @@
|
|||
|
||||
#import <SOGo/SOGoParentFolder.h>
|
||||
|
||||
@class NSArray;
|
||||
|
||||
@interface SOGoAppointmentFolders : SOGoParentFolder
|
||||
|
||||
- (NSArray *) proxyFoldersWithWriteAccess: (BOOL) hasWriteAccess;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOAPPOINTMENTFOLDERS_H */
|
||||
|
|
|
@ -26,17 +26,26 @@
|
|||
#import <Foundation/NSEnumerator.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <NGObjWeb/WOContext.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
#import <NGObjWeb/WORequest+So.h>
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
|
||||
#import <SaxObjC/XMLNamespaces.h>
|
||||
|
||||
#import <SOGo/WORequest+SOGo.h>
|
||||
#import <SOGo/NSObject+DAV.h>
|
||||
#import <SOGo/SOGoWebDAVValue.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import "SOGoAppointmentFolder.h"
|
||||
|
||||
#import "SOGoAppointmentFolders.h"
|
||||
|
||||
@interface SOGoParentFolder (Private)
|
||||
|
||||
- (NSException *) initSubscribedSubFolders;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SOGoAppointmentFolders
|
||||
|
||||
+ (NSString *) gcsFolderType
|
||||
|
@ -54,6 +63,36 @@
|
|||
return [self labelForKey: @"Personal Calendar"];
|
||||
}
|
||||
|
||||
- (NSArray *) toManyRelationshipKeys
|
||||
{
|
||||
NSEnumerator *sortedSubFolders;
|
||||
SOGoGCSFolder *currentFolder;
|
||||
NSString *login;
|
||||
NSMutableArray *keys;
|
||||
|
||||
login = [[context activeUser] login];
|
||||
if ([[context request] isICal])
|
||||
{
|
||||
keys = [NSMutableArray array];
|
||||
if ([owner isEqualToString: login])
|
||||
{
|
||||
sortedSubFolders = [[self subFolders] objectEnumerator];
|
||||
while ((currentFolder = [sortedSubFolders nextObject]))
|
||||
{
|
||||
if ([[currentFolder ownerInContext: context]
|
||||
isEqualToString: owner])
|
||||
[keys addObject: [currentFolder nameInContainer]];
|
||||
}
|
||||
}
|
||||
else
|
||||
[keys addObject: @"personal"];
|
||||
}
|
||||
else
|
||||
keys = (NSMutableArray *) [super toManyRelationshipKeys];
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
- (NSString *) _fetchPropertyWithName: (NSString *) propertyName
|
||||
inArray: (NSArray *) section
|
||||
{
|
||||
|
@ -199,4 +238,35 @@
|
|||
return componentSet;
|
||||
}
|
||||
|
||||
- (NSArray *) proxyFoldersWithWriteAccess: (BOOL) hasWriteAccess
|
||||
{
|
||||
NSMutableArray *proxyFolders;
|
||||
NSArray *proxySubscribers;
|
||||
NSEnumerator *folders;
|
||||
SOGoAppointmentFolder *currentFolder;
|
||||
NSString *folderOwner, *currentUser;
|
||||
|
||||
proxyFolders = [NSMutableArray new];
|
||||
|
||||
currentUser = [[context activeUser] login];
|
||||
|
||||
[self initSubscribedSubFolders];
|
||||
folders = [subscribedSubFolders objectEnumerator];
|
||||
while ((currentFolder = [folders nextObject]))
|
||||
{
|
||||
folderOwner = [currentFolder ownerInContext: context];
|
||||
/* we currently only list the users of which we have subscribed to the
|
||||
personal folder */
|
||||
if ([[currentFolder realNameInContainer] isEqualToString: @"personal"])
|
||||
{
|
||||
proxySubscribers
|
||||
= [currentFolder proxySubscribersWithWriteAccess: hasWriteAccess];
|
||||
if ([proxySubscribers containsObject: currentUser])
|
||||
[proxyFolders addObject: currentFolder];
|
||||
}
|
||||
}
|
||||
|
||||
return proxyFolders;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/* SOGoCalendarProxy.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 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 SOGOCALENDARPROXY_H
|
||||
#define SOGOCALENDARPROXY_H
|
||||
|
||||
#import <SOGo/SOGoFolder.h>
|
||||
|
||||
@interface SOGoCalendarProxy : SOGoFolder
|
||||
{
|
||||
BOOL hasWriteAccess;
|
||||
}
|
||||
|
||||
- (void) setWriteAccess: (BOOL) newHasWriteAccess;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOCALENDARPROXY_H */
|
|
@ -0,0 +1,156 @@
|
|||
/* SOGoCalendarProxy.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 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 <Foundation/NSArray.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
|
||||
#import "SOGoAppointmentFolder.h"
|
||||
#import "SOGoCalendarProxy.h"
|
||||
|
||||
@implementation SOGoCalendarProxy
|
||||
|
||||
#define XMLNS_CALENDARSERVER @"http://calendarserver.org/ns/"
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
hasWriteAccess = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) setWriteAccess: (BOOL) newHasWriteAccess
|
||||
{
|
||||
hasWriteAccess = newHasWriteAccess;
|
||||
}
|
||||
|
||||
- (NSArray *) davResourceType
|
||||
{
|
||||
NSString *proxyType;
|
||||
NSMutableArray *rType;
|
||||
|
||||
rType = [NSMutableArray arrayWithArray: [super davResourceType]];
|
||||
[rType addObject: @"principal"];
|
||||
if (hasWriteAccess)
|
||||
proxyType = @"calendar-proxy-write";
|
||||
else
|
||||
proxyType = @"calendar-proxy-read";
|
||||
[rType addObject: [NSArray arrayWithObjects: proxyType,
|
||||
XMLNS_CALENDARSERVER, nil]];
|
||||
|
||||
return rType;
|
||||
}
|
||||
|
||||
- (NSArray *) davGroupMemberSet
|
||||
{
|
||||
NSMutableArray *members;
|
||||
NSEnumerator *subscribers;
|
||||
NSArray *member;
|
||||
SOGoUser *ownerUser;
|
||||
SOGoAppointmentFolder *folder;
|
||||
NSString *subscriber;
|
||||
|
||||
members = [NSMutableArray array];
|
||||
|
||||
ownerUser = [SOGoUser userWithLogin: [self ownerInContext: context]
|
||||
roles: nil];
|
||||
folder = [ownerUser personalCalendarFolderInContext: context];
|
||||
subscribers = [[folder proxySubscribersWithWriteAccess: hasWriteAccess]
|
||||
objectEnumerator];
|
||||
while ((subscriber = [subscribers nextObject]))
|
||||
{
|
||||
member = [NSArray arrayWithObjects: @"href", @"DAV:", @"D",
|
||||
[NSString stringWithFormat: @"/SOGo/dav/%@/",
|
||||
subscriber],
|
||||
nil];
|
||||
[members addObject: member];
|
||||
}
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
- (NSString *) davGroupMembership
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) _parseSubscriber: (NSString *) memberSet
|
||||
until: (int) length
|
||||
{
|
||||
int begin, end;
|
||||
NSRange beginRange;
|
||||
|
||||
end = length;
|
||||
if ([memberSet characterAtIndex: end - 1] == '/')
|
||||
end--;
|
||||
beginRange = [memberSet rangeOfString: @"/"
|
||||
options: NSBackwardsSearch
|
||||
range: NSMakeRange (0, end)];
|
||||
begin = NSMaxRange (beginRange);
|
||||
|
||||
return [memberSet substringWithRange: NSMakeRange (begin, end - begin)];
|
||||
}
|
||||
|
||||
- (NSArray *) _parseSubscribers: (NSString *) memberSet
|
||||
{
|
||||
NSRange endRange;
|
||||
NSMutableArray *subscribers;
|
||||
NSMutableString *mMemberSet;
|
||||
NSString *subscriber;
|
||||
|
||||
subscribers = [NSMutableArray array];
|
||||
mMemberSet = [NSMutableString stringWithString: memberSet];
|
||||
|
||||
endRange = [mMemberSet rangeOfString: @"</"];
|
||||
while (endRange.location != NSNotFound)
|
||||
{
|
||||
subscriber = [self _parseSubscriber: mMemberSet
|
||||
until: endRange.location];
|
||||
[subscribers addObjectUniquely: subscriber];
|
||||
[mMemberSet
|
||||
deleteCharactersInRange: NSMakeRange (0, endRange.location + 1)];
|
||||
endRange = [mMemberSet rangeOfString: @"</"];
|
||||
}
|
||||
|
||||
return subscribers;
|
||||
}
|
||||
|
||||
- (NSException *) setDavGroupMemberSet: (NSString *) memberSet
|
||||
{
|
||||
SOGoUser *ownerUser;
|
||||
SOGoAppointmentFolder *folder;
|
||||
|
||||
ownerUser = [SOGoUser userWithLogin: [self ownerInContext: context]
|
||||
roles: nil];
|
||||
folder = [ownerUser personalCalendarFolderInContext: context];
|
||||
|
||||
return [folder setProxySubscribers: [self _parseSubscribers: memberSet]
|
||||
withWriteAccess: hasWriteAccess];
|
||||
}
|
||||
|
||||
@end
|
|
@ -30,10 +30,12 @@
|
|||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <SaxObjC/XMLNamespaces.h>
|
||||
|
||||
#import <SOGo/SOGoGCSFolder.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/NSObject+DAV.h>
|
||||
#import <SOGo/NSString+DAV.h>
|
||||
|
||||
#import "SOGoAppointmentFolders.h"
|
||||
#import "SOGoUserFolder+Appointments.h"
|
||||
|
||||
@interface SOGoUserFolder (private)
|
||||
|
@ -431,4 +433,47 @@
|
|||
// </D:prop>
|
||||
}
|
||||
|
||||
- (void) _addFolders: (NSEnumerator *) folders
|
||||
withGroupTag: (NSString *) groupTag
|
||||
toArray: (NSMutableArray *) groups
|
||||
{
|
||||
SOGoAppointmentFolder *currentFolder;
|
||||
NSString *folderOwner;
|
||||
NSArray *tag;
|
||||
|
||||
while ((currentFolder = [folders nextObject]))
|
||||
{
|
||||
folderOwner = [currentFolder ownerInContext: context];
|
||||
tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D",
|
||||
[NSString stringWithFormat: @"/SOGo/dav/%@/%@/",
|
||||
folderOwner, groupTag],
|
||||
nil];
|
||||
[groups addObject: tag];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *) davGroupMembership
|
||||
{
|
||||
SOGoAppointmentFolders *calendars;
|
||||
NSArray *writeFolders, *readFolders;
|
||||
NSMutableArray *groups;
|
||||
|
||||
groups = [NSMutableArray array];
|
||||
|
||||
[self ownerInContext: context];
|
||||
|
||||
calendars = [self privateCalendars: @"Calendar" inContext: context];
|
||||
writeFolders = [calendars proxyFoldersWithWriteAccess: YES];
|
||||
[self _addFolders: [writeFolders objectEnumerator]
|
||||
withGroupTag: @"calendar-proxy-write"
|
||||
toArray: groups];
|
||||
|
||||
readFolders = [calendars proxyFoldersWithWriteAccess: NO];
|
||||
[self _addFolders: [readFolders objectEnumerator]
|
||||
withGroupTag: @"calendar-proxy-read"
|
||||
toArray: groups];
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -245,6 +245,11 @@
|
|||
return rType;
|
||||
}
|
||||
|
||||
- (BOOL) davIsCollection
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
/* web dav acl helper */
|
||||
- (void) _fillArrayWithPrincipalsOwnedBySelf: (NSMutableArray *) hrefs
|
||||
{
|
||||
|
@ -263,6 +268,17 @@
|
|||
acquire: NO] _fillArrayWithPrincipalsOwnedBySelf: hrefs];
|
||||
}
|
||||
|
||||
|
||||
- (NSArray *) davGroupMemberSet
|
||||
{
|
||||
return [NSArray array];
|
||||
}
|
||||
|
||||
- (NSArray *) davGroupMembership
|
||||
{
|
||||
return [NSArray array];
|
||||
}
|
||||
|
||||
/* folder type */
|
||||
|
||||
- (BOOL) isEqual: (id) otherFolder
|
||||
|
|
|
@ -87,10 +87,9 @@
|
|||
- (NSException *) delete;
|
||||
- (void) renameTo: (NSString *) newName;
|
||||
|
||||
- (WOResponse *) subscribe: (BOOL) reallyDo
|
||||
inTheNamesOf: (NSArray *) delegatedUsers
|
||||
fromMailInvitation: (BOOL) isMailInvitation
|
||||
inContext: (WOContext *) localContext;
|
||||
- (BOOL) subscribeUser: (NSString *) subscribingUser
|
||||
reallyDo: (BOOL) reallyDo;
|
||||
- (BOOL) userIsSubscriber: (NSString *) subscribingUser;
|
||||
|
||||
- (void) initializeQuickTablesAclsInContext: (WOContext *) localContext;
|
||||
|
||||
|
|
|
@ -650,26 +650,34 @@ static NSArray *childRecordFields = nil;
|
|||
return cTag;
|
||||
}
|
||||
|
||||
#warning this code should be cleaned up
|
||||
- (void) _subscribeUser: (SOGoUser *) subscribingUser
|
||||
- (BOOL) userIsSubscriber: (NSString *) subscribingUser
|
||||
{
|
||||
SOGoUser *sogoUser;
|
||||
NSDictionary *moduleSettings;
|
||||
NSArray *folderSubscription;
|
||||
|
||||
sogoUser = [SOGoUser userWithLogin: subscribingUser roles: nil];
|
||||
moduleSettings = [[sogoUser userSettings]
|
||||
objectForKey: [container nameInContainer]];
|
||||
folderSubscription = [moduleSettings objectForKey: @"SubscribedFolders"];
|
||||
|
||||
return [folderSubscription containsObject: [self folderReference]];
|
||||
}
|
||||
|
||||
- (BOOL) subscribeUser: (NSString *) subscribingUser
|
||||
reallyDo: (BOOL) reallyDo
|
||||
fromMailInvitation: (BOOL) isMailInvitation
|
||||
inResponse: (WOResponse *) response
|
||||
{
|
||||
NSMutableArray *folderSubscription, *tmpA;
|
||||
NSString *subscriptionPointer, *mailInvitationURL;
|
||||
NSString *subscriptionPointer;
|
||||
NSUserDefaults *ud;
|
||||
NSMutableDictionary *moduleSettings, *tmpD;
|
||||
SOGoUser *sogoUser;
|
||||
BOOL rc;
|
||||
|
||||
if ([owner isEqualToString: [subscribingUser login]])
|
||||
sogoUser = [SOGoUser userWithLogin: subscribingUser roles: nil];
|
||||
if (sogoUser)
|
||||
{
|
||||
[response setStatus: 403];
|
||||
[response appendContentString:
|
||||
@"You cannot (un)subscribe to a folder that you own!"];
|
||||
}
|
||||
else
|
||||
{
|
||||
ud = [subscribingUser userSettings];
|
||||
ud = [sogoUser userSettings];
|
||||
moduleSettings = [ud objectForKey: [container nameInContainer]];
|
||||
if (!(moduleSettings
|
||||
&& [moduleSettings isKindOfClass: [NSMutableDictionary class]]))
|
||||
|
@ -717,82 +725,21 @@ static NSArray *childRecordFields = nil;
|
|||
}
|
||||
|
||||
[ud synchronize];
|
||||
|
||||
if (isMailInvitation)
|
||||
{
|
||||
mailInvitationURL = [[self soURLToBaseContainerForCurrentUser]
|
||||
absoluteString];
|
||||
[response setStatus: 302];
|
||||
[response setHeader: mailInvitationURL
|
||||
forKey: @"location"];
|
||||
rc = YES;
|
||||
}
|
||||
else
|
||||
[response setStatus: 204];
|
||||
}
|
||||
rc = NO;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (WOResponse *) subscribe: (BOOL) reallyDo
|
||||
inTheNamesOf: (NSArray *) delegatedUsers
|
||||
fromMailInvitation: (BOOL) isMailInvitation
|
||||
inContext: (WOContext *) localContext
|
||||
{
|
||||
WOResponse *response;
|
||||
SOGoUser *currentUser;
|
||||
|
||||
response = [localContext response];
|
||||
[response setHeader: @"text/plain; charset=utf-8"
|
||||
forKey: @"Content-Type"];
|
||||
|
||||
currentUser = [localContext activeUser];
|
||||
|
||||
if ([delegatedUsers count])
|
||||
{
|
||||
if (![currentUser isSuperUser])
|
||||
{
|
||||
[response setStatus: 403];
|
||||
[response appendContentString:
|
||||
@"You cannot subscribe another user to any folder"
|
||||
@" unless you are a super-user."];
|
||||
}
|
||||
else
|
||||
{
|
||||
// The current user is a superuser...
|
||||
SOGoUser *subscriptionUser;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < [delegatedUsers count]; i++)
|
||||
{
|
||||
// We trust the passed user ID here as it might generate tons or LDAP
|
||||
// call but more importantly, cache propagation calls that will create
|
||||
// contention on GDNC.
|
||||
subscriptionUser = [SOGoUser userWithLogin: [delegatedUsers objectAtIndex: i]
|
||||
roles: nil
|
||||
trust: YES];
|
||||
|
||||
[self _subscribeUser: subscriptionUser
|
||||
reallyDo: reallyDo
|
||||
fromMailInvitation: isMailInvitation
|
||||
inResponse: response];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self _subscribeUser: currentUser
|
||||
reallyDo: reallyDo
|
||||
fromMailInvitation: isMailInvitation
|
||||
inResponse: response];
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (NSArray *) _parseDAVDelegatedUser: (WOContext *) queryContext
|
||||
- (NSArray *) _parseDAVDelegatedUsers
|
||||
{
|
||||
id <DOMDocument> document;
|
||||
id <DOMNamedNodeMap> attrs;
|
||||
id o;
|
||||
document = [[queryContext request] contentAsDOMDocument];
|
||||
|
||||
document = [[context request] contentAsDOMDocument];
|
||||
attrs = [[document documentElement] attributes];
|
||||
|
||||
o = [attrs namedItem: @"users"];
|
||||
|
@ -802,20 +749,65 @@ static NSArray *childRecordFields = nil;
|
|||
return nil;
|
||||
}
|
||||
|
||||
- (WOResponse *) _davSubscribe: (BOOL) reallyDo
|
||||
{
|
||||
WOResponse *response;
|
||||
SOGoUser *currentUser;
|
||||
NSArray *delegatedUsers;
|
||||
NSString *userLogin;
|
||||
int count, max;
|
||||
|
||||
response = [context response];
|
||||
[response setHeader: @"text/plain; charset=utf-8"
|
||||
forKey: @"Content-Type"];
|
||||
[response setStatus: 204];
|
||||
|
||||
currentUser = [context activeUser];
|
||||
delegatedUsers = [self _parseDAVDelegatedUsers];
|
||||
|
||||
max = [delegatedUsers count];
|
||||
if (max)
|
||||
{
|
||||
if ([currentUser isSuperUser])
|
||||
{
|
||||
/* We trust the passed user ID here as it might generate tons or
|
||||
LDAP call but more importantly, cache propagation calls that will
|
||||
create contention on GDNC. */
|
||||
for (count = 0; count < max; count++)
|
||||
[self subscribeUser: [delegatedUsers objectAtIndex: count]
|
||||
reallyDo: reallyDo];
|
||||
}
|
||||
else
|
||||
{
|
||||
[response setStatus: 403];
|
||||
[response appendContentString: @"You cannot subscribe another user"
|
||||
@" to any folder unless you are a super-user."];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
userLogin = [currentUser login];
|
||||
if ([owner isEqualToString: userLogin])
|
||||
{
|
||||
[response setStatus: 403];
|
||||
[response appendContentString:
|
||||
@"You cannot (un)subscribe to a folder that you own!"];
|
||||
}
|
||||
else
|
||||
[self subscribeUser: userLogin reallyDo: reallyDo];
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) davSubscribe: (WOContext *) queryContext
|
||||
{
|
||||
return [self subscribe: YES
|
||||
inTheNamesOf: [self _parseDAVDelegatedUser: queryContext]
|
||||
fromMailInvitation: NO
|
||||
inContext: queryContext];
|
||||
return [self _davSubscribe: YES];
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) davUnsubscribe: (WOContext *) queryContext
|
||||
{
|
||||
return [self subscribe: NO
|
||||
inTheNamesOf: [self _parseDAVDelegatedUser: queryContext]
|
||||
fromMailInvitation: NO
|
||||
inContext: queryContext];
|
||||
return [self _davSubscribe: NO];
|
||||
}
|
||||
|
||||
- (NSDictionary *) davSQLFieldsTable
|
||||
|
|
|
@ -1653,7 +1653,7 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
|
|||
}
|
||||
else
|
||||
exception
|
||||
= [NSException exceptionWithHTTPStatus: 404
|
||||
= [NSException exceptionWithHTTPStatus: 403
|
||||
reason: [NSString stringWithFormat:
|
||||
@"Property '%@' cannot be set.",
|
||||
currentProp]];
|
||||
|
|
|
@ -107,6 +107,7 @@ static SoSecurityManager *sm = nil;
|
|||
{
|
||||
subFolders = nil;
|
||||
OCSPath = nil;
|
||||
subscribedSubFolders = nil;
|
||||
subFolderClass = Nil;
|
||||
// hasSubscribedSources = NO;
|
||||
}
|
||||
|
@ -116,6 +117,7 @@ static SoSecurityManager *sm = nil;
|
|||
|
||||
- (void) dealloc
|
||||
{
|
||||
[subscribedSubFolders release];
|
||||
[subFolders release];
|
||||
[OCSPath release];
|
||||
[super dealloc];
|
||||
|
@ -353,27 +355,19 @@ static SoSecurityManager *sm = nil;
|
|||
|
||||
- (NSException *) initSubscribedSubFolders
|
||||
{
|
||||
NSArray *subscribedReferences;
|
||||
NSUserDefaults *settings;
|
||||
NSEnumerator *allKeys;
|
||||
NSString *currentKey, *login;
|
||||
NSString *login;
|
||||
NSException *error;
|
||||
|
||||
if (!subFolderClass)
|
||||
subFolderClass = [[self class] subFolderClass];
|
||||
|
||||
error = nil; /* we ignore non-DB errors at this time... */
|
||||
login = [[context activeUser] login];
|
||||
|
||||
if (!subscribedSubFolders && [login isEqualToString: owner])
|
||||
{
|
||||
subscribedSubFolders = [NSMutableDictionary new];
|
||||
settings = [[context activeUser] userSettings];
|
||||
subscribedReferences = [[settings objectForKey: nameInContainer]
|
||||
objectForKey: @"SubscribedFolders"];
|
||||
if ([subscribedReferences isKindOfClass: [NSArray class]])
|
||||
{
|
||||
allKeys = [subscribedReferences objectEnumerator];
|
||||
while ((currentKey = [allKeys nextObject]))
|
||||
[self _appendSubscribedSource: currentKey];
|
||||
}
|
||||
error = [self appendSubscribedSources];
|
||||
}
|
||||
|
||||
return error;
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
|
||||
#import <Appointments/SOGoAppointmentFolders.h>
|
||||
#import <Appointments/SOGoFreeBusyObject.h>
|
||||
#import <Appointments/SOGoCalendarProxy.h>
|
||||
#import <Contacts/SOGoContactFolders.h>
|
||||
#import <Mailer/SOGoMailAccounts.h>
|
||||
|
||||
|
@ -53,6 +54,7 @@
|
|||
#import "LDAPUserManager.h"
|
||||
#import "SOGoPermissions.h"
|
||||
#import "SOGoUser.h"
|
||||
#import "WORequest+SOGo.h"
|
||||
|
||||
#import "SOGoUserFolder.h"
|
||||
|
||||
|
@ -84,11 +86,21 @@ static NSString *LDAPContactInfoAttribute = nil;
|
|||
|
||||
currentUser = [context activeUser];
|
||||
if ([currentUser canAccessModule: @"Calendar"])
|
||||
{
|
||||
[children addObject: @"Calendar"];
|
||||
/* support for caldav-proxy, which is currently limited to iCal but may
|
||||
be enabled for others later, once we sort out the consistency between
|
||||
subscribe folders and "proxy collections". */
|
||||
if ([[context request] isICal])
|
||||
{
|
||||
[children addObject: @"calendar-proxy-write"];
|
||||
[children addObject: @"calendar-proxy-read"];
|
||||
}
|
||||
}
|
||||
[children addObject: @"Contacts"];
|
||||
if ([currentUser canAccessModule: @"Mail"])
|
||||
[children addObject: @"Mail"];
|
||||
[children addObject: @"Preferences"];
|
||||
// [children addObject: @"Preferences"];
|
||||
|
||||
return children;
|
||||
}
|
||||
|
@ -529,6 +541,17 @@ static NSString *LDAPContactInfoAttribute = nil;
|
|||
return [$(@"SOGoFreeBusyObject") objectWithName: _key inContainer: self];
|
||||
}
|
||||
|
||||
- (id) calendarProxy: (NSString *) name withWriteAccess: (BOOL) hasWrite
|
||||
{
|
||||
id calendarProxy;
|
||||
|
||||
calendarProxy = [$(@"SOGoCalendarProxy") objectWithName: name
|
||||
inContainer: self];
|
||||
[calendarProxy setWriteAccess: hasWrite];
|
||||
|
||||
return calendarProxy;
|
||||
}
|
||||
|
||||
- (id) lookupName: (NSString *) _key
|
||||
inContext: (WOContext *) _ctx
|
||||
acquire: (BOOL) _flag
|
||||
|
@ -541,23 +564,29 @@ static NSString *LDAPContactInfoAttribute = nil;
|
|||
if (!obj)
|
||||
{
|
||||
currentUser = [_ctx activeUser];
|
||||
if ([currentUser canAccessModule: @"Calendar"])
|
||||
{
|
||||
if ([_key isEqualToString: @"Calendar"])
|
||||
{
|
||||
if ([currentUser canAccessModule: _key])
|
||||
obj = [self privateCalendars: @"Calendar" inContext: _ctx];
|
||||
}
|
||||
else if ([_key isEqualToString: @"Contacts"])
|
||||
obj = [self privateContacts: _key inContext: _ctx];
|
||||
else if ([_key isEqualToString: @"Mail"])
|
||||
{
|
||||
if ([currentUser canAccessModule: _key])
|
||||
obj = [self mailAccountsFolder: _key inContext: _ctx];
|
||||
}
|
||||
else if ([_key isEqualToString: @"Preferences"])
|
||||
obj = [$(@"SOGoPreferencesFolder") objectWithName: _key
|
||||
inContainer: self];
|
||||
else if ([_key isEqualToString: @"freebusy.ifb"])
|
||||
obj = [self freeBusyObject:_key inContext: _ctx];
|
||||
else if ([_key isEqualToString: @"calendar-proxy-write"])
|
||||
obj = [self calendarProxy: _key withWriteAccess: YES];
|
||||
else if ([_key isEqualToString: @"calendar-proxy-read"])
|
||||
obj = [self calendarProxy: _key withWriteAccess: NO];
|
||||
}
|
||||
|
||||
if (!obj
|
||||
&& [_key isEqualToString: @"Mail"]
|
||||
&& [currentUser canAccessModule: @"Mail"])
|
||||
obj = [self mailAccountsFolder: _key inContext: _ctx];
|
||||
|
||||
if (!obj && [_key isEqualToString: @"Contacts"])
|
||||
obj = [self privateContacts: _key inContext: _ctx];
|
||||
|
||||
// else if ([_key isEqualToString: @"Preferences"])
|
||||
// obj = [$(@"SOGoPreferencesFolder") objectWithName: _key
|
||||
// inContainer: self];
|
||||
|
||||
if (!obj)
|
||||
obj = [NSException exceptionWithHTTPStatus: 404 /* Not Found */];
|
||||
|
@ -584,9 +613,16 @@ static NSString *LDAPContactInfoAttribute = nil;
|
|||
getCNForUID: nameInContainer];
|
||||
}
|
||||
|
||||
- (BOOL) davIsCollection
|
||||
- (NSArray *) davPrincipalURL
|
||||
{
|
||||
return YES;
|
||||
NSArray *principalURL;
|
||||
NSString *selfDAVPath;
|
||||
|
||||
selfDAVPath = [[self davURL] path];
|
||||
principalURL = [NSArray arrayWithObjects: @"href", @"DAV:", @"D",
|
||||
selfDAVPath, nil];
|
||||
|
||||
return [NSArray arrayWithObject: principalURL];
|
||||
}
|
||||
|
||||
@end /* SOGoUserFolder */
|
||||
|
|
|
@ -29,7 +29,10 @@
|
|||
|
||||
- (BOOL) handledByDefaultHandler;
|
||||
- (NSDictionary *) davPatchedPropertiesWithTopTag: (NSString *) topTag;
|
||||
|
||||
- (BOOL) isAppleDAVWithSubstring: (NSString *) osSubstring;
|
||||
- (BOOL) isIPhone;
|
||||
- (BOOL) isICal;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -103,22 +103,32 @@
|
|||
return patchedProperties;
|
||||
}
|
||||
|
||||
- (BOOL) isIPhone
|
||||
- (BOOL) isAppleDAVWithSubstring: (NSString *) osSubstring
|
||||
{
|
||||
WEClientCapabilities *cc;
|
||||
BOOL rc;
|
||||
NSRange r;
|
||||
|
||||
rc = NO;
|
||||
cc = [self clientCapabilities];
|
||||
if ([[cc userAgentType] isEqualToString: @"AppleDAVAccess"])
|
||||
{
|
||||
NSRange r = [[cc userAgent] rangeOfString: @"iPhone"];
|
||||
if (r.location != NSNotFound)
|
||||
rc = YES;
|
||||
r = [[cc userAgent] rangeOfString: osSubstring];
|
||||
rc = (r.location != NSNotFound);
|
||||
}
|
||||
else
|
||||
rc = NO;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (BOOL) isIPhone
|
||||
{
|
||||
return [self isAppleDAVWithSubstring: @"iPhone/"];
|
||||
}
|
||||
|
||||
- (BOOL) isICal
|
||||
{
|
||||
return [self isAppleDAVWithSubstring: @"Mac OS X/10."];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -76,24 +76,48 @@
|
|||
isMailInvitation = [mailInvitationParam boolValue];
|
||||
}
|
||||
|
||||
- (WOResponse *) _subscribeAction: (BOOL) reallyDo
|
||||
{
|
||||
WOResponse *response;
|
||||
NSURL *mailInvitationURL;
|
||||
|
||||
response = [context response];
|
||||
[response setHeader: @"text/plain; charset=utf-8"
|
||||
forKey: @"Content-Type"];
|
||||
|
||||
[self _setupContext];
|
||||
if ([owner isEqualToString: login])
|
||||
{
|
||||
[response setStatus: 403];
|
||||
[response appendContentString:
|
||||
@"You cannot (un)subscribe to a folder that you own!"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[clientObject subscribeUser: login reallyDo: reallyDo];
|
||||
if (isMailInvitation)
|
||||
{
|
||||
mailInvitationURL
|
||||
= [clientObject soURLToBaseContainerForCurrentUser];
|
||||
[response setStatus: 302];
|
||||
[response setHeader: [mailInvitationURL absoluteString]
|
||||
forKey: @"location"];
|
||||
}
|
||||
else
|
||||
[response setStatus: 204];
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (WOResponse *) subscribeAction
|
||||
{
|
||||
[self _setupContext];
|
||||
|
||||
return [clientObject subscribe: YES
|
||||
inTheNamesOf: nil
|
||||
fromMailInvitation: isMailInvitation
|
||||
inContext: context];
|
||||
return [self _subscribeAction: YES];
|
||||
}
|
||||
|
||||
- (WOResponse *) unsubscribeAction
|
||||
{
|
||||
[self _setupContext];
|
||||
|
||||
return [clientObject subscribe: NO
|
||||
inTheNamesOf: nil
|
||||
fromMailInvitation: isMailInvitation
|
||||
inContext: context];
|
||||
return [self _subscribeAction: NO];
|
||||
}
|
||||
|
||||
- (WOResponse *) canAccessContentAction
|
||||
|
|
Loading…
Reference in New Issue