Monotone-Parent: a8698cc119066cef660b176c3e956b5d0d843e7f

Monotone-Revision: f5b8996b9296ee950ea712c8de2c37e474163519

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2009-08-27T21:14:54
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Wolfgang Sourdeau 2009-08-27 21:14:54 +00:00
parent a38722d9cb
commit 06b296ddba
19 changed files with 698 additions and 145 deletions

View File

@ -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
View File

@ -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)
--------------------

View File

@ -28,6 +28,8 @@ Appointments_OBJC_FILES = \
SOGoFreeBusyObject.m \
SOGoUserFolder+Appointments.m \
\
SOGoCalendarProxy.m \
\
SOGoAptMailNotification.m \
SOGoAptMailInvitation.m \
SOGoAptMailDeletion.m \

View File

@ -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__ */

View File

@ -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 */

View File

@ -25,8 +25,12 @@
#import <SOGo/SOGoParentFolder.h>
@class NSArray;
@interface SOGoAppointmentFolders : SOGoParentFolder
- (NSArray *) proxyFoldersWithWriteAccess: (BOOL) hasWriteAccess;
@end
#endif /* SOGOAPPOINTMENTFOLDERS_H */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -650,26 +650,34 @@ static NSArray *childRecordFields = nil;
return cTag;
}
#warning this code should be cleaned up
- (void) _subscribeUser: (SOGoUser *) subscribingUser
reallyDo: (BOOL) reallyDo
fromMailInvitation: (BOOL) isMailInvitation
inResponse: (WOResponse *) response
- (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
{
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]]))
@ -687,7 +695,7 @@ static NSArray *childRecordFields = nil;
[moduleSettings setObject: folderSubscription
forKey: @"SubscribedFolders"];
}
subscriptionPointer = [self folderReference];
if (reallyDo)
[folderSubscription addObjectUniquely: subscriptionPointer];
@ -717,82 +725,21 @@ static NSArray *childRecordFields = nil;
}
[ud synchronize];
if (isMailInvitation)
{
mailInvitationURL = [[self soURLToBaseContainerForCurrentUser]
absoluteString];
[response setStatus: 302];
[response setHeader: mailInvitationURL
forKey: @"location"];
}
else
[response setStatus: 204];
}
}
- (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];
}
}
rc = YES;
}
else
{
[self _subscribeUser: currentUser
reallyDo: reallyDo
fromMailInvitation: isMailInvitation
inResponse: response];
}
rc = NO;
return response;
return rc;
}
- (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

View File

@ -1653,7 +1653,7 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
}
else
exception
= [NSException exceptionWithHTTPStatus: 404
= [NSException exceptionWithHTTPStatus: 403
reason: [NSString stringWithFormat:
@"Property '%@' cannot be set.",
currentProp]];

View File

@ -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,29 +355,21 @@ 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;
}

View File

@ -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"];
{
[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 ([_key isEqualToString: @"Calendar"])
{
if ([currentUser canAccessModule: _key])
if ([currentUser canAccessModule: @"Calendar"])
{
if ([_key isEqualToString: @"Calendar"])
obj = [self privateCalendars: @"Calendar" inContext: _ctx];
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];
}
else if ([_key isEqualToString: @"Contacts"])
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: @"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: @"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 */

View File

@ -29,7 +29,10 @@
- (BOOL) handledByDefaultHandler;
- (NSDictionary *) davPatchedPropertiesWithTopTag: (NSString *) topTag;
- (BOOL) isAppleDAVWithSubstring: (NSString *) osSubstring;
- (BOOL) isIPhone;
- (BOOL) isICal;
@end

View File

@ -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

View File

@ -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