2008-05-03 01:08:15 +02:00
|
|
|
/* SOGoGCSFolder.m - this file is part of SOGo
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004-2005 SKYRIX Software AG
|
2008-09-27 03:13:41 +02:00
|
|
|
* Copyright (C) 2006-2008 Inverse inc.
|
2008-05-03 01:08:15 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2007-05-14 19:26:25 +02:00
|
|
|
|
2007-09-14 23:59:00 +02:00
|
|
|
#import <unistd.h>
|
|
|
|
|
|
|
|
#import <Foundation/NSBundle.h>
|
2007-05-30 22:18:42 +02:00
|
|
|
#import <Foundation/NSClassDescription.h>
|
2008-04-24 03:14:21 +02:00
|
|
|
#import <Foundation/NSFileManager.h>
|
|
|
|
#import <Foundation/NSPathUtilities.h>
|
2007-05-30 22:18:42 +02:00
|
|
|
#import <Foundation/NSString.h>
|
|
|
|
#import <Foundation/NSUserDefaults.h>
|
|
|
|
#import <Foundation/NSURL.h>
|
|
|
|
#import <Foundation/NSValue.h>
|
|
|
|
|
2007-11-09 17:12:24 +01:00
|
|
|
#import <NGObjWeb/SoClass.h>
|
2006-09-08 17:14:07 +02:00
|
|
|
#import <NGObjWeb/SoObject+SoDAV.h>
|
2008-04-24 03:14:21 +02:00
|
|
|
#import <NGObjWeb/SoSelectorInvocation.h>
|
2007-05-30 22:18:42 +02:00
|
|
|
#import <NGObjWeb/WEClientCapabilities.h>
|
|
|
|
#import <NGObjWeb/WOApplication.h>
|
2007-02-17 00:24:06 +01:00
|
|
|
#import <NGObjWeb/WOContext.h>
|
2007-09-14 23:59:00 +02:00
|
|
|
#import <NGObjWeb/WOResourceManager.h>
|
2006-12-14 22:20:13 +01:00
|
|
|
#import <NGObjWeb/WOResponse.h>
|
2007-02-17 00:24:06 +01:00
|
|
|
#import <NGObjWeb/WORequest.h>
|
2007-05-30 22:18:42 +02:00
|
|
|
#import <NGObjWeb/WORequest+So.h>
|
|
|
|
#import <NGObjWeb/NSException+HTTP.h>
|
|
|
|
#import <NGExtensions/NSObject+Logs.h>
|
2007-06-01 23:04:56 +02:00
|
|
|
#import <NGExtensions/NSString+misc.h>
|
2008-02-22 22:19:29 +01:00
|
|
|
#import <DOM/DOMDocument.h>
|
|
|
|
#import <DOM/DOMNode.h>
|
2007-12-05 00:28:52 +01:00
|
|
|
#import <DOM/DOMProtocols.h>
|
2006-12-14 22:20:13 +01:00
|
|
|
#import <NGCards/NSDictionary+NGCards.h>
|
2007-05-30 22:18:42 +02:00
|
|
|
#import <UI/SOGoUI/SOGoACLAdvisory.h>
|
2006-12-14 22:20:13 +01:00
|
|
|
|
2008-02-22 22:19:29 +01:00
|
|
|
#import "LDAPUserManager.h"
|
2007-05-30 22:18:42 +02:00
|
|
|
#import "NSArray+Utilities.h"
|
2007-12-06 23:47:31 +01:00
|
|
|
#import "NSCalendarDate+SOGo.h"
|
2007-09-14 23:59:00 +02:00
|
|
|
#import "NSDictionary+Utilities.h"
|
2008-05-03 01:08:15 +02:00
|
|
|
#import "NSObject+DAV.h"
|
2008-04-24 03:14:21 +02:00
|
|
|
#import "NSObject+Utilities.h"
|
2007-05-30 22:18:42 +02:00
|
|
|
#import "NSString+Utilities.h"
|
2008-01-16 19:57:58 +01:00
|
|
|
#import "SOGoCache.h"
|
2008-02-22 22:19:29 +01:00
|
|
|
#import "SOGoPermissions.h"
|
|
|
|
#import "SOGoUser.h"
|
|
|
|
#import "SOGoUserFolder.h"
|
2008-05-03 01:08:15 +02:00
|
|
|
#import "SOGoWebDAVAclManager.h"
|
|
|
|
#import "SOGoWebDAVValue.h"
|
2008-02-22 22:19:29 +01:00
|
|
|
|
2006-12-14 22:20:13 +01:00
|
|
|
#import "SOGoObject.h"
|
2006-10-03 18:10:55 +02:00
|
|
|
|
2008-02-22 22:19:29 +01:00
|
|
|
static BOOL kontactGroupDAV = YES;
|
|
|
|
static BOOL sendACLAdvisories = NO;
|
|
|
|
|
2008-04-24 03:14:21 +02:00
|
|
|
static NSDictionary *reportMap = nil;
|
2008-07-09 23:09:08 +02:00
|
|
|
static NSMutableDictionary *setterMap = nil;
|
|
|
|
static NSMutableDictionary *getterMap = nil;
|
|
|
|
|
|
|
|
SEL SOGoSelectorForPropertyGetter (NSString *property)
|
|
|
|
{
|
|
|
|
SEL propSel;
|
|
|
|
NSValue *propPtr;
|
|
|
|
NSDictionary *map;
|
|
|
|
NSString *methodName;
|
|
|
|
|
|
|
|
if (!getterMap)
|
|
|
|
getterMap = [NSMutableDictionary new];
|
|
|
|
propPtr = [getterMap objectForKey: property];
|
|
|
|
if (propPtr)
|
|
|
|
propSel = [propPtr pointerValue];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
map = [SOGoObject defaultWebDAVAttributeMap];
|
|
|
|
methodName = [map objectForKey: property];
|
|
|
|
if (methodName)
|
|
|
|
{
|
|
|
|
propSel = NSSelectorFromString (methodName);
|
|
|
|
if (propSel)
|
|
|
|
[getterMap setObject: [NSValue valueWithPointer: propSel]
|
|
|
|
forKey: property];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return propSel;
|
|
|
|
}
|
|
|
|
|
|
|
|
SEL SOGoSelectorForPropertySetter (NSString *property)
|
|
|
|
{
|
|
|
|
SEL propSel;
|
|
|
|
NSValue *propPtr;
|
|
|
|
NSDictionary *map;
|
|
|
|
NSString *methodName;
|
|
|
|
|
|
|
|
if (!setterMap)
|
|
|
|
setterMap = [NSMutableDictionary new];
|
|
|
|
propPtr = [setterMap objectForKey: property];
|
|
|
|
if (propPtr)
|
|
|
|
propSel = [propPtr pointerValue];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
map = [SOGoObject defaultWebDAVAttributeMap];
|
|
|
|
methodName = [map objectForKey: property];
|
|
|
|
if (methodName)
|
|
|
|
{
|
|
|
|
propSel = NSSelectorFromString ([methodName davSetterName]);
|
|
|
|
if (propSel)
|
|
|
|
[setterMap setObject: [NSValue valueWithPointer: propSel]
|
|
|
|
forKey: property];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return propSel;
|
|
|
|
}
|
2008-04-24 03:14:21 +02:00
|
|
|
|
2008-05-03 01:08:15 +02:00
|
|
|
@implementation SOGoObject
|
2006-12-14 22:20:13 +01:00
|
|
|
|
2008-05-03 01:08:15 +02:00
|
|
|
+ (SOGoWebDAVAclManager *) webdavAclManager
|
2006-12-14 22:20:13 +01:00
|
|
|
{
|
2008-12-16 00:03:04 +01:00
|
|
|
static SOGoWebDAVAclManager *aclManager = nil;
|
2006-12-14 22:20:13 +01:00
|
|
|
|
2008-07-29 18:36:16 +02:00
|
|
|
if (!aclManager)
|
|
|
|
aclManager = [SOGoWebDAVAclManager new];
|
2007-01-04 21:01:46 +01:00
|
|
|
|
2008-07-29 18:36:16 +02:00
|
|
|
return aclManager;
|
2006-12-14 22:20:13 +01:00
|
|
|
}
|
|
|
|
|
2008-05-03 01:08:15 +02:00
|
|
|
/*
|
|
|
|
+ (id) WebDAVPermissionsMap
|
2006-12-14 22:20:13 +01:00
|
|
|
{
|
2008-05-03 01:08:15 +02:00
|
|
|
static NSDictionary *permissions = nil;
|
2006-12-14 22:20:13 +01:00
|
|
|
|
2008-05-03 01:08:15 +02:00
|
|
|
if (!permissions)
|
2006-12-14 22:20:13 +01:00
|
|
|
{
|
2008-05-03 01:08:15 +02:00
|
|
|
permissions = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
davElement (@"read", @"DAV:"),
|
|
|
|
SoPerm_AccessContentsInformation,
|
|
|
|
davElement (@"bind", @"DAV:"),
|
|
|
|
SoPerm_AddDocumentsImagesAndFiles,
|
|
|
|
davElement (@"unbind", @"DAV:"),
|
|
|
|
SoPerm_DeleteObjects,
|
|
|
|
davElement (@"write-acl", @"DAV:"),
|
|
|
|
SoPerm_ChangePermissions,
|
|
|
|
davElement (@"write-content", @"DAV:"),
|
|
|
|
SoPerm_ChangeImagesAndFiles, NULL];
|
|
|
|
[permissions retain];
|
2006-12-14 22:20:13 +01:00
|
|
|
}
|
|
|
|
|
2008-05-03 01:08:15 +02:00
|
|
|
return permissions;
|
|
|
|
} */
|
2008-04-24 03:14:21 +02:00
|
|
|
|
2006-09-08 17:14:07 +02:00
|
|
|
+ (void) initialize
|
|
|
|
{
|
2008-02-22 22:19:29 +01:00
|
|
|
NSUserDefaults *ud;
|
2008-05-03 01:08:15 +02:00
|
|
|
NSString *filename;
|
|
|
|
NSBundle *bundle;
|
2006-12-14 22:20:13 +01:00
|
|
|
|
2008-02-22 22:19:29 +01:00
|
|
|
ud = [NSUserDefaults standardUserDefaults];
|
|
|
|
kontactGroupDAV = ![ud boolForKey:@"SOGoDisableKontact34GroupDAVHack"];
|
|
|
|
sendACLAdvisories = [ud boolForKey: @"SOGoACLsSendEMailNotifications"];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2008-04-24 03:14:21 +02:00
|
|
|
if (!reportMap)
|
2008-05-03 01:08:15 +02:00
|
|
|
{
|
|
|
|
bundle = [NSBundle bundleForClass: self];
|
|
|
|
filename = [bundle pathForResource: @"DAVReportMap" ofType: @"plist"];
|
|
|
|
if (filename
|
|
|
|
&& [[NSFileManager defaultManager] fileExistsAtPath: filename])
|
|
|
|
reportMap = [[NSDictionary alloc] initWithContentsOfFile: filename];
|
|
|
|
else
|
|
|
|
[self logWithFormat: @"DAV REPORT map not found!"];
|
|
|
|
}
|
2007-04-26 03:16:19 +02:00
|
|
|
// SoClass security declarations
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2007-04-26 03:16:19 +02:00
|
|
|
// require View permission to access the root (bound to authenticated ...)
|
2007-07-30 17:56:58 +02:00
|
|
|
// [[self soClassSecurityInfo] declareObjectProtected: SoPerm_View];
|
2006-12-14 22:20:13 +01:00
|
|
|
|
2007-04-26 03:16:19 +02:00
|
|
|
// to allow public access to all contained objects (subkeys)
|
2007-07-30 17:56:58 +02:00
|
|
|
// [[self soClassSecurityInfo] setDefaultAccess: @"allow"];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2007-04-26 03:16:19 +02:00
|
|
|
// /* require Authenticated role for View and WebDAV */
|
|
|
|
// [[self soClassSecurityInfo] declareRole: SoRole_Owner
|
|
|
|
// asDefaultForPermission: SoPerm_View];
|
|
|
|
// [[self soClassSecurityInfo] declareRole: SoRole_Owner
|
|
|
|
// asDefaultForPermission: SoPerm_WebDAVAccess];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-09-14 23:59:00 +02:00
|
|
|
+ (NSString *) globallyUniqueObjectId
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
4C08AE1A-A808-11D8-AC5A-000393BBAFF6
|
|
|
|
SOGo-Web-28273-18283-288182
|
|
|
|
printf( "%x", *(int *) &f);
|
|
|
|
*/
|
|
|
|
static int pid = 0;
|
|
|
|
static int sequence = 0;
|
|
|
|
static float rndm = 0;
|
|
|
|
float f;
|
|
|
|
|
|
|
|
if (pid == 0)
|
2008-02-22 22:19:29 +01:00
|
|
|
{
|
2007-09-14 23:59:00 +02:00
|
|
|
pid = getpid();
|
|
|
|
rndm = random();
|
|
|
|
}
|
|
|
|
sequence++;
|
|
|
|
f = [[NSDate date] timeIntervalSince1970];
|
|
|
|
|
|
|
|
return [NSString stringWithFormat:@"%0X-%0X-%0X-%0X",
|
|
|
|
pid, (int) f, sequence++, random];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) globallyUniqueObjectId
|
|
|
|
{
|
|
|
|
return [[self class] globallyUniqueObjectId];
|
|
|
|
}
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* containment */
|
|
|
|
|
2006-08-21 19:43:36 +02:00
|
|
|
+ (id) objectWithName: (NSString *)_name inContainer:(id)_container
|
|
|
|
{
|
|
|
|
SOGoObject *object;
|
|
|
|
|
|
|
|
object = [[self alloc] initWithName: _name inContainer: _container];
|
|
|
|
[object autorelease];
|
|
|
|
|
|
|
|
return object;
|
|
|
|
}
|
|
|
|
|
2006-12-14 22:20:13 +01:00
|
|
|
/* end of properties */
|
|
|
|
|
2007-04-11 21:03:42 +02:00
|
|
|
- (BOOL) doesRetainContainer
|
|
|
|
{
|
2008-12-16 00:03:04 +01:00
|
|
|
return NO;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-06-01 06:07:13 +02:00
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]))
|
2007-04-27 21:03:33 +02:00
|
|
|
{
|
2007-06-01 06:07:13 +02:00
|
|
|
context = nil;
|
|
|
|
nameInContainer = nil;
|
|
|
|
container = nil;
|
|
|
|
owner = nil;
|
2008-05-03 01:08:15 +02:00
|
|
|
webdavAclManager = [[self class] webdavAclManager];
|
2008-06-13 22:08:45 +02:00
|
|
|
activeUserIsOwner = NO;
|
2007-04-27 21:03:33 +02:00
|
|
|
}
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
return self;
|
|
|
|
}
|
2006-08-21 19:43:36 +02:00
|
|
|
|
2007-06-01 06:07:13 +02:00
|
|
|
- (id) initWithName: (NSString *) _name
|
|
|
|
inContainer: (id) _container
|
2007-04-27 21:03:33 +02:00
|
|
|
{
|
2007-06-01 06:07:13 +02:00
|
|
|
if ((self = [self init]))
|
2007-04-27 21:03:33 +02:00
|
|
|
{
|
2007-06-01 06:07:13 +02:00
|
|
|
context = [[WOApplication application] context];
|
|
|
|
nameInContainer = [_name copy];
|
|
|
|
container = _container;
|
|
|
|
if ([self doesRetainContainer])
|
|
|
|
[_container retain];
|
2007-06-02 00:16:20 +02:00
|
|
|
owner = [self ownerInContext: context];
|
|
|
|
if (owner)
|
|
|
|
[owner retain];
|
2007-04-27 21:03:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-06-01 06:07:13 +02:00
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
[owner release];
|
2006-06-15 21:34:10 +02:00
|
|
|
if ([self doesRetainContainer])
|
2006-09-08 17:14:07 +02:00
|
|
|
[container release];
|
|
|
|
[nameInContainer release];
|
2006-06-15 21:34:10 +02:00
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* accessors */
|
|
|
|
|
2007-06-01 23:04:56 +02:00
|
|
|
- (NSString *) nameInContainer
|
|
|
|
{
|
2006-09-08 17:14:07 +02:00
|
|
|
return nameInContainer;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
2007-06-01 23:04:56 +02:00
|
|
|
|
|
|
|
- (id) container
|
|
|
|
{
|
2006-09-08 17:14:07 +02:00
|
|
|
return container;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ownership */
|
|
|
|
|
2006-12-19 18:03:41 +01:00
|
|
|
- (void) setOwner: (NSString *) newOwner
|
|
|
|
{
|
2007-06-01 06:07:13 +02:00
|
|
|
ASSIGN (owner, newOwner);
|
2006-12-19 18:03:41 +01:00
|
|
|
}
|
|
|
|
|
2007-06-01 06:07:13 +02:00
|
|
|
- (NSString *) ownerInContext: (id) localContext
|
|
|
|
{
|
2008-06-13 22:08:45 +02:00
|
|
|
NSString *uid;
|
|
|
|
|
2007-06-02 00:16:20 +02:00
|
|
|
if (!owner)
|
2008-06-13 22:08:45 +02:00
|
|
|
{
|
|
|
|
owner = [container ownerInContext: context];
|
|
|
|
uid = [[localContext activeUser] login];
|
|
|
|
activeUserIsOwner = [owner isEqualToString: uid];
|
|
|
|
}
|
2007-06-02 00:16:20 +02:00
|
|
|
|
2007-06-01 06:07:13 +02:00
|
|
|
return owner;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* hierarchy */
|
|
|
|
|
2007-06-01 06:07:13 +02:00
|
|
|
- (NSArray *) fetchSubfolders
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
NSMutableArray *ma;
|
|
|
|
NSArray *names;
|
|
|
|
unsigned i, count;
|
|
|
|
|
|
|
|
if ((names = [self toManyRelationshipKeys]) == nil)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
count = [names count];
|
|
|
|
ma = [NSMutableArray arrayWithCapacity:count + 1];
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
id folder;
|
2006-12-14 22:20:13 +01:00
|
|
|
|
|
|
|
folder = [self lookupName: [names objectAtIndex:i]
|
|
|
|
inContext: nil
|
|
|
|
acquire: NO];
|
2006-06-15 21:34:10 +02:00
|
|
|
if (folder == nil)
|
|
|
|
continue;
|
|
|
|
if ([folder isKindOfClass:[NSException class]])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
[ma addObject:folder];
|
|
|
|
}
|
|
|
|
return ma;
|
|
|
|
}
|
|
|
|
|
2008-04-24 03:14:21 +02:00
|
|
|
- (NSString *) _reportSelector: (NSString *) reportName
|
|
|
|
{
|
|
|
|
NSString *methodName, *objcMethod, *resultName;
|
|
|
|
SEL reportSel;
|
|
|
|
|
|
|
|
resultName = nil;
|
|
|
|
|
|
|
|
methodName = [reportMap objectForKey: reportName];
|
|
|
|
if (methodName)
|
|
|
|
{
|
|
|
|
objcMethod = [NSString stringWithFormat: @"%@:", methodName];
|
|
|
|
reportSel = NSSelectorFromString (objcMethod);
|
|
|
|
if ([self respondsToSelector: reportSel])
|
|
|
|
resultName = objcMethod;
|
|
|
|
}
|
|
|
|
|
|
|
|
return resultName;
|
|
|
|
}
|
|
|
|
|
2007-11-09 17:12:24 +01:00
|
|
|
- (id) lookupName: (NSString *) lookupName
|
|
|
|
inContext: (id) localContext
|
|
|
|
acquire: (BOOL) acquire
|
|
|
|
{
|
|
|
|
id obj;
|
2008-01-16 19:57:58 +01:00
|
|
|
SOGoCache *cache;
|
2008-05-03 01:08:15 +02:00
|
|
|
NSString *objcMethod, *httpMethod;
|
2007-11-09 17:12:24 +01:00
|
|
|
|
2008-01-16 19:57:58 +01:00
|
|
|
cache = [SOGoCache sharedCache];
|
|
|
|
obj = [cache objectNamed: lookupName inContainer: self];
|
|
|
|
if (!obj)
|
|
|
|
{
|
2008-05-03 01:08:15 +02:00
|
|
|
httpMethod = [[localContext request] method];
|
|
|
|
if ([httpMethod isEqualToString: @"REPORT"])
|
2008-01-16 19:57:58 +01:00
|
|
|
{
|
2008-04-24 03:14:21 +02:00
|
|
|
objcMethod = [self _reportSelector: lookupName];
|
|
|
|
if (objcMethod)
|
|
|
|
{
|
|
|
|
obj = [[SoSelectorInvocation alloc]
|
|
|
|
initWithSelectorNamed: objcMethod
|
|
|
|
addContextParameter: YES];
|
|
|
|
[obj autorelease];
|
|
|
|
}
|
2008-01-16 19:57:58 +01:00
|
|
|
}
|
2008-05-03 01:08:15 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
obj = [[self soClass] lookupKey: lookupName inContext: localContext];
|
|
|
|
if (obj)
|
2008-09-11 19:56:02 +02:00
|
|
|
[obj bindToObject: self inContext: localContext];
|
2008-05-03 01:08:15 +02:00
|
|
|
}
|
2008-04-24 03:14:21 +02:00
|
|
|
|
|
|
|
if (obj)
|
|
|
|
[cache registerObject: obj withName: lookupName inContainer: self];
|
2008-01-16 19:57:58 +01:00
|
|
|
}
|
2007-11-09 17:12:24 +01:00
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* looking up shared objects */
|
|
|
|
|
2007-09-14 23:59:00 +02:00
|
|
|
- (SOGoUserFolder *) lookupUserFolder
|
|
|
|
{
|
2006-09-08 17:14:07 +02:00
|
|
|
if (![container respondsToSelector:_cmd])
|
2006-06-15 21:34:10 +02:00
|
|
|
return nil;
|
|
|
|
|
2006-09-08 17:14:07 +02:00
|
|
|
return [container lookupUserFolder];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
2007-06-01 06:07:13 +02:00
|
|
|
|
2008-05-03 01:08:15 +02:00
|
|
|
// - (SOGoGroupsFolder *) lookupGroupsFolder
|
|
|
|
// {
|
|
|
|
// return [[self lookupUserFolder] lookupGroupsFolder];
|
|
|
|
// }
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2007-06-01 06:07:13 +02:00
|
|
|
- (void) sleep
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
if ([self doesRetainContainer])
|
2006-09-08 17:14:07 +02:00
|
|
|
[container release];
|
|
|
|
container = nil;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* operations */
|
|
|
|
|
2007-06-01 06:07:13 +02:00
|
|
|
- (NSException *) delete
|
|
|
|
{
|
|
|
|
return [NSException exceptionWithHTTPStatus: 501 /* not implemented */
|
|
|
|
reason: @"delete not yet implemented, sorry ..."];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* KVC hacks */
|
|
|
|
|
2007-06-01 06:07:13 +02:00
|
|
|
- (id) valueForUndefinedKey: (NSString *) _key
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* WebDAV */
|
|
|
|
|
2007-06-01 06:07:13 +02:00
|
|
|
- (NSString *) davDisplayName
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
return [self nameInContainer];
|
|
|
|
}
|
|
|
|
|
2008-05-03 01:08:15 +02:00
|
|
|
/* DAV ACL properties */
|
|
|
|
- (SOGoWebDAVValue *) davOwner
|
|
|
|
{
|
|
|
|
NSDictionary *ownerHREF;
|
|
|
|
NSString *usersUrl;
|
|
|
|
|
|
|
|
usersUrl = [NSString stringWithFormat: @"%@%@/",
|
|
|
|
[[WOApplication application] davURL], owner];
|
|
|
|
ownerHREF = davElementWithContent (@"href", @"DAV:", usersUrl);
|
|
|
|
|
|
|
|
return [davElementWithContent (@"owner", @"DAV:", ownerHREF)
|
|
|
|
asWebDAVValue];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (SOGoWebDAVValue *) davAclRestrictions
|
|
|
|
{
|
|
|
|
NSArray *restrictions;
|
|
|
|
|
|
|
|
restrictions = [NSArray arrayWithObjects:
|
|
|
|
davElement (@"grant-only", @"DAV:"),
|
|
|
|
davElement (@"no-invert", @"DAV:"),
|
|
|
|
nil];
|
|
|
|
|
|
|
|
return [davElementWithContent (@"acl-restrictions", @"DAV:", restrictions)
|
|
|
|
asWebDAVValue];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (SOGoWebDAVValue *) davPrincipalCollectionSet
|
|
|
|
{
|
|
|
|
NSString *usersUrl;
|
|
|
|
NSDictionary *collectionHREF;
|
|
|
|
|
|
|
|
/* WOApplication has no support for the DAV methods we define here so we
|
|
|
|
use the user's principal object as a reference */
|
|
|
|
usersUrl = [NSString stringWithFormat: @"%@%@/",
|
|
|
|
[[WOApplication application] davURL], owner];
|
|
|
|
collectionHREF = davElementWithContent (@"href", @"DAV:", usersUrl);
|
|
|
|
|
|
|
|
return [davElementWithContent (@"principal-collection-set",
|
|
|
|
@"DAV:",
|
|
|
|
[NSArray arrayWithObject: collectionHREF])
|
|
|
|
asWebDAVValue];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *) _davPrivilegesFromRoles: (NSArray *) roles
|
|
|
|
{
|
|
|
|
NSEnumerator *privileges;
|
|
|
|
NSDictionary *privilege;
|
|
|
|
NSMutableArray *davPrivileges;
|
|
|
|
|
|
|
|
davPrivileges = [NSMutableArray array];
|
|
|
|
|
|
|
|
privileges = [[webdavAclManager davPermissionsForRoles: roles
|
|
|
|
onObject: self] objectEnumerator];
|
|
|
|
while ((privilege = [privileges nextObject]))
|
|
|
|
[davPrivileges addObject: davElementWithContent (@"privilege", @"DAV:",
|
|
|
|
privilege)];
|
|
|
|
|
|
|
|
return davPrivileges;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (SOGoWebDAVValue *) davCurrentUserPrivilegeSet
|
|
|
|
{
|
|
|
|
NSArray *userRoles;
|
|
|
|
|
|
|
|
userRoles = [[context activeUser] rolesForObject: self inContext: context];
|
|
|
|
|
|
|
|
return [davElementWithContent (@"current-user-privilege-set",
|
|
|
|
@"DAV:",
|
|
|
|
[self _davPrivilegesFromRoles: userRoles])
|
|
|
|
asWebDAVValue];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (SOGoWebDAVValue *) davSupportedPrivilegeSet
|
|
|
|
{
|
|
|
|
return [davElementWithContent (@"supported-privilege-set",
|
|
|
|
@"DAV:",
|
|
|
|
[webdavAclManager treeAsWebDAVValue])
|
|
|
|
asWebDAVValue];
|
|
|
|
}
|
|
|
|
|
|
|
|
#warning this method has probably some code shared with its pseudo principal equivalent
|
|
|
|
- (void) _fillAces: (NSMutableArray *) aces
|
|
|
|
withRolesForUID: (NSString *) currentUID
|
|
|
|
{
|
|
|
|
NSMutableArray *currentAce;
|
|
|
|
NSArray *roles;
|
|
|
|
NSDictionary *currentGrant, *userHREF;
|
|
|
|
NSString *principalURL;
|
|
|
|
|
2009-06-17 17:19:19 +02:00
|
|
|
currentAce = [NSMutableArray array];
|
2008-05-03 01:08:15 +02:00
|
|
|
roles = [[SOGoUser userWithLogin: currentUID roles: nil]
|
|
|
|
rolesForObject: self
|
|
|
|
inContext: context];
|
|
|
|
if ([roles count])
|
|
|
|
{
|
|
|
|
principalURL = [NSString stringWithFormat: @"%@%@/",
|
|
|
|
[[WOApplication application] davURL],
|
|
|
|
currentUID];
|
|
|
|
userHREF = davElementWithContent (@"href", @"DAV:", principalURL);
|
|
|
|
[currentAce addObject: davElementWithContent (@"principal", @"DAV:",
|
|
|
|
userHREF)];
|
|
|
|
currentGrant
|
|
|
|
= davElementWithContent (@"grant", @"DAV:",
|
|
|
|
[self _davPrivilegesFromRoles: roles]);
|
|
|
|
[currentAce addObject: currentGrant];
|
|
|
|
[aces addObject: davElementWithContent (@"ace", @"DAV:", currentAce)];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _fillAcesWithRolesForPseudoPrincipals: (NSMutableArray *) aces
|
|
|
|
{
|
|
|
|
NSArray *roles, *currentAce;
|
|
|
|
NSDictionary *principal, *currentGrant;
|
|
|
|
SOGoUser *user;
|
|
|
|
|
|
|
|
// DAV:self, DAV:property(owner), DAV:authenticated
|
|
|
|
user = [context activeUser];
|
|
|
|
roles = [user rolesForObject: self inContext: context];
|
|
|
|
if ([roles count])
|
|
|
|
{
|
|
|
|
principal = davElement (@"self", @"DAV:");
|
|
|
|
currentGrant
|
|
|
|
= davElementWithContent (@"grant", @"DAV:",
|
|
|
|
[self _davPrivilegesFromRoles: roles]);
|
|
|
|
currentAce = [NSArray arrayWithObjects:
|
|
|
|
davElementWithContent (@"principal", @"DAV:",
|
|
|
|
principal),
|
|
|
|
currentGrant, nil];
|
|
|
|
[aces addObject: davElementWithContent (@"ace", @"DAV:", currentAce)];
|
|
|
|
}
|
|
|
|
|
|
|
|
user = [SOGoUser userWithLogin: [self ownerInContext: context] roles: nil];
|
|
|
|
roles = [user rolesForObject: self inContext: context];
|
|
|
|
if ([roles count])
|
|
|
|
{
|
|
|
|
principal = davElementWithContent (@"property", @"DAV:",
|
|
|
|
davElement (@"owner", @"DAV:"));
|
|
|
|
currentGrant
|
|
|
|
= davElementWithContent (@"grant", @"DAV:",
|
|
|
|
[self _davPrivilegesFromRoles: roles]);
|
|
|
|
currentAce = [NSArray arrayWithObjects:
|
|
|
|
davElementWithContent (@"principal", @"DAV:",
|
|
|
|
principal),
|
|
|
|
currentGrant, nil];
|
|
|
|
[aces addObject: davElementWithContent (@"ace", @"DAV:", currentAce)];
|
|
|
|
}
|
|
|
|
|
|
|
|
roles = [self aclsForUser: [self defaultUserID]];
|
|
|
|
if ([roles count])
|
|
|
|
{
|
|
|
|
principal = davElement (@"authenticated", @"DAV:");
|
|
|
|
currentGrant
|
|
|
|
= davElementWithContent (@"grant", @"DAV:",
|
|
|
|
[self _davPrivilegesFromRoles: roles]);
|
|
|
|
currentAce = [NSArray arrayWithObjects:
|
|
|
|
davElementWithContent (@"principal", @"DAV:",
|
|
|
|
principal),
|
|
|
|
currentGrant, nil];
|
|
|
|
[aces addObject: davElementWithContent (@"ace", @"DAV:", currentAce)];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (SOGoWebDAVValue *) davAcl
|
|
|
|
{
|
|
|
|
NSEnumerator *uids;
|
|
|
|
NSMutableArray *aces;
|
|
|
|
NSString *currentUID;
|
|
|
|
|
|
|
|
aces = [NSMutableArray array];
|
|
|
|
|
|
|
|
[self _fillAcesWithRolesForPseudoPrincipals: aces];
|
|
|
|
uids = [[self aclUsers] objectEnumerator];
|
|
|
|
while ((currentUID = [uids nextObject]))
|
|
|
|
[self _fillAces: aces withRolesForUID: currentUID];
|
|
|
|
|
|
|
|
return [davElementWithContent (@"acl", @"DAV:", aces)
|
|
|
|
asWebDAVValue];
|
|
|
|
}
|
|
|
|
|
|
|
|
#warning all REPORT method should be standardized...
|
|
|
|
- (NSDictionary *) _formalizePrincipalMatchResponse: (NSArray *) hrefs
|
|
|
|
{
|
|
|
|
NSDictionary *multiStatus;
|
|
|
|
NSEnumerator *hrefList;
|
|
|
|
NSString *currentHref;
|
|
|
|
NSMutableArray *responses;
|
|
|
|
NSArray *responseElements;
|
|
|
|
|
2009-06-17 17:19:19 +02:00
|
|
|
responses = [NSMutableArray array];
|
2008-05-03 01:08:15 +02:00
|
|
|
|
|
|
|
hrefList = [hrefs objectEnumerator];
|
|
|
|
while ((currentHref = [hrefList nextObject]))
|
|
|
|
{
|
|
|
|
responseElements
|
|
|
|
= [NSArray arrayWithObjects: davElementWithContent (@"href", @"DAV:",
|
|
|
|
currentHref),
|
|
|
|
davElementWithContent (@"status", @"DAV:",
|
|
|
|
@"HTTP/1.1 200 OK"),
|
|
|
|
nil];
|
|
|
|
[responses addObject: davElementWithContent (@"response", @"DAV:",
|
|
|
|
responseElements)];
|
|
|
|
}
|
|
|
|
|
|
|
|
multiStatus = davElementWithContent (@"multistatus", @"DAV:", responses);
|
|
|
|
|
|
|
|
return multiStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary *) _handlePrincipalMatchSelf
|
|
|
|
{
|
|
|
|
NSString *davURL, *userLogin;
|
|
|
|
NSArray *principalURL;
|
|
|
|
|
|
|
|
davURL = [[WOApplication application] davURL];
|
|
|
|
userLogin = [[context activeUser] login];
|
|
|
|
principalURL
|
|
|
|
= [NSArray arrayWithObject: [NSString stringWithFormat: @"%@%@/", davURL,
|
|
|
|
userLogin]];
|
|
|
|
return [self _formalizePrincipalMatchResponse: principalURL];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _fillArrayWithPrincipalsOwnedBySelf: (NSMutableArray *) hrefs
|
|
|
|
{
|
|
|
|
NSString *url;
|
|
|
|
NSArray *roles;
|
|
|
|
|
|
|
|
roles = [[context activeUser] rolesForObject: self inContext: context];
|
|
|
|
if ([roles containsObject: SoRole_Owner])
|
|
|
|
{
|
|
|
|
url = [[self davURL] absoluteString];
|
|
|
|
[hrefs addObject: url];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary *)
|
|
|
|
_handlePrincipalMatchPrincipalProperty: (id <DOMElement>) child
|
|
|
|
{
|
|
|
|
NSMutableArray *hrefs;
|
|
|
|
NSDictionary *response;
|
|
|
|
|
2009-06-17 17:19:19 +02:00
|
|
|
hrefs = [NSMutableArray array];
|
2008-05-03 01:08:15 +02:00
|
|
|
[self _fillArrayWithPrincipalsOwnedBySelf: hrefs];
|
|
|
|
|
|
|
|
response = [self _formalizePrincipalMatchResponse: hrefs];
|
|
|
|
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary *) _handlePrincipalMatchReport: (id <DOMDocument>) document
|
|
|
|
{
|
|
|
|
NSDictionary *response;
|
|
|
|
id <DOMElement> documentElement, queryChild;
|
|
|
|
NSArray *children;
|
|
|
|
NSString *queryTag;
|
|
|
|
|
|
|
|
documentElement = [document documentElement];
|
|
|
|
children = [self domNode: documentElement
|
|
|
|
getChildNodesByType: DOM_ELEMENT_NODE];
|
|
|
|
if ([children count] == 1)
|
|
|
|
{
|
|
|
|
queryChild = [children objectAtIndex: 0];
|
|
|
|
queryTag = [queryChild tagName];
|
|
|
|
if ([queryTag isEqualToString: @"self"])
|
|
|
|
response = [self _handlePrincipalMatchSelf];
|
|
|
|
else if ([queryTag isEqualToString: @"principal-property"])
|
|
|
|
response = [self _handlePrincipalMatchPrincipalProperty: queryChild];
|
|
|
|
else
|
|
|
|
response = [NSException exceptionWithHTTPStatus: 400
|
|
|
|
reason: @"Query element must be either "
|
|
|
|
@" '{DAV:}principal-property' or '{DAV:}self'"];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
response = [NSException exceptionWithHTTPStatus: 400
|
|
|
|
reason: @"Query must have one element:"
|
|
|
|
@" '{DAV:}principal-property' or '{DAV:}self'"];
|
|
|
|
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (WOResponse *) davPrincipalMatch: (WOContext *) localContext
|
|
|
|
{
|
|
|
|
WOResponse *r;
|
|
|
|
id <DOMDocument> document;
|
|
|
|
NSDictionary *xmlResponse;
|
|
|
|
|
|
|
|
r = [context response];
|
|
|
|
|
|
|
|
document = [[context request] contentAsDOMDocument];
|
|
|
|
xmlResponse = [self _handlePrincipalMatchReport: document];
|
|
|
|
if ([xmlResponse isKindOfClass: [NSException class]])
|
|
|
|
r = (WOResponse *) xmlResponse;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[r setStatus: 207];
|
|
|
|
[r setContentEncoding: NSUTF8StringEncoding];
|
|
|
|
[r setHeader: @"text/xml; charset=\"utf-8\"" forKey: @"content-type"];
|
|
|
|
[r setHeader: @"no-cache" forKey: @"pragma"];
|
|
|
|
[r setHeader: @"no-cache" forKey: @"cache-control"];
|
|
|
|
[r appendContentString:
|
|
|
|
@"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"];
|
|
|
|
[r appendContentString: [xmlResponse asWebDavStringWithNamespaces: nil]];
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* actions */
|
|
|
|
|
2007-06-01 06:07:13 +02:00
|
|
|
- (id) DELETEAction: (id) _ctx
|
|
|
|
{
|
2008-04-24 03:14:21 +02:00
|
|
|
id result;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2008-04-24 03:14:21 +02:00
|
|
|
result = [self delete];
|
2006-06-15 21:34:10 +02:00
|
|
|
/* Note: returning 'nil' breaks in SoObjectRequestHandler */
|
2008-04-24 03:14:21 +02:00
|
|
|
if (!result)
|
|
|
|
result = [NSNumber numberWithBool: YES]; /* delete worked out ... */
|
|
|
|
|
|
|
|
return result;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2008-03-04 21:57:36 +01:00
|
|
|
- (BOOL) isFolderish
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) davIsCollection
|
|
|
|
{
|
|
|
|
return [self isFolderish];
|
|
|
|
}
|
|
|
|
|
2007-06-01 06:07:13 +02:00
|
|
|
- (NSString *) davContentType
|
|
|
|
{
|
|
|
|
return @"text/plain";
|
|
|
|
}
|
|
|
|
|
2007-12-06 23:47:31 +01:00
|
|
|
- (NSString *) davLastModified
|
|
|
|
{
|
|
|
|
return [[NSCalendarDate date] rfc822DateString];
|
|
|
|
}
|
|
|
|
|
2007-06-01 06:07:13 +02:00
|
|
|
- (WOResponse *) _webDAVResponse: (WOContext *) localContext
|
|
|
|
{
|
|
|
|
WOResponse *response;
|
|
|
|
NSString *contentType;
|
|
|
|
id etag;
|
|
|
|
|
|
|
|
response = [localContext response];
|
|
|
|
contentType = [NSString stringWithFormat: @"%@; charset=utf8",
|
|
|
|
[self davContentType]];
|
|
|
|
[response setHeader: contentType forKey: @"content-type"];
|
|
|
|
[response appendContentString: [self contentAsString]];
|
|
|
|
etag = [self davEntityTag];
|
|
|
|
if (etag)
|
|
|
|
[response setHeader: etag forKey: @"etag"];
|
|
|
|
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
2007-12-05 00:28:52 +01:00
|
|
|
- (NSString *) _parseXMLCommand: (id <DOMDocument>) document
|
|
|
|
{
|
|
|
|
NSString *command;
|
|
|
|
|
2008-05-06 01:41:24 +02:00
|
|
|
command = [[document firstChild] localName];
|
2007-12-05 00:28:52 +01:00
|
|
|
|
|
|
|
return [NSString stringWithFormat: @"%@:", command];
|
|
|
|
}
|
|
|
|
|
2008-07-04 20:22:06 +02:00
|
|
|
- (id) davPOSTRequest: (WORequest *) request
|
|
|
|
withContentType: (NSString *) cType
|
|
|
|
inContext: (WOContext *) localContext
|
2007-12-05 00:28:52 +01:00
|
|
|
{
|
|
|
|
id obj;
|
|
|
|
id <DOMDocument> document;
|
|
|
|
SEL commandSel;
|
2008-07-04 20:22:06 +02:00
|
|
|
NSString *command;
|
2007-12-05 00:28:52 +01:00
|
|
|
|
|
|
|
obj = nil;
|
|
|
|
|
2008-07-04 20:22:06 +02:00
|
|
|
if ([cType hasPrefix: @"application/xml"]
|
|
|
|
|| [cType hasPrefix: @"text/xml"])
|
2007-12-05 00:28:52 +01:00
|
|
|
{
|
2008-07-04 20:22:06 +02:00
|
|
|
document = [request contentAsDOMDocument];
|
|
|
|
command = [[self _parseXMLCommand: document] davMethodToObjC];
|
|
|
|
commandSel = NSSelectorFromString (command);
|
|
|
|
if ([self respondsToSelector: commandSel])
|
|
|
|
obj = [self performSelector: commandSel withObject: localContext];
|
2007-12-05 00:28:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2008-07-04 20:22:06 +02:00
|
|
|
- (id) POSTAction: (id) localContext
|
|
|
|
{
|
|
|
|
WORequest *rq;
|
|
|
|
id obj;
|
|
|
|
|
|
|
|
rq = [localContext request];
|
|
|
|
if ([rq isSoWebDAVRequest])
|
|
|
|
obj = [self davPOSTRequest: rq
|
|
|
|
withContentType: [rq headerForKey: @"content-type"]
|
|
|
|
inContext: localContext];
|
|
|
|
else
|
|
|
|
obj = nil;
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2007-06-01 06:07:13 +02:00
|
|
|
- (id) GETAction: (id) localContext
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
// TODO: I guess this should really be done by SOPE (redirect to
|
|
|
|
// default method)
|
2007-06-01 06:07:13 +02:00
|
|
|
WORequest *request;
|
2006-10-03 18:10:55 +02:00
|
|
|
NSString *uri;
|
2007-06-01 06:07:13 +02:00
|
|
|
NSException *error;
|
|
|
|
id value;
|
|
|
|
|
2008-08-22 20:43:08 +02:00
|
|
|
request = [localContext request];
|
2007-06-01 06:07:13 +02:00
|
|
|
if ([request isSoWebDAVRequest])
|
|
|
|
{
|
|
|
|
if ([self respondsToSelector: @selector (contentAsString)])
|
|
|
|
{
|
|
|
|
error = [self matchesRequestConditionInContext: localContext];
|
|
|
|
if (error)
|
|
|
|
value = error;
|
|
|
|
else
|
|
|
|
value = [self _webDAVResponse: localContext];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
value = [NSException exceptionWithHTTPStatus: 501 /* not implemented */
|
|
|
|
reason: @"no WebDAV GET support?!"];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
2007-06-01 06:07:13 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
value = [localContext response];
|
|
|
|
uri = [[request uri] composeURLWithAction: @"view"
|
|
|
|
parameters: [request formValues]
|
|
|
|
andHash: NO];
|
|
|
|
[value setStatus: 302 /* moved */];
|
|
|
|
[value setHeader: uri forKey: @"location"];
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* etag support */
|
|
|
|
|
|
|
|
- (NSArray *)parseETagList:(NSString *)_c {
|
|
|
|
NSMutableArray *ma;
|
|
|
|
NSArray *etags;
|
|
|
|
unsigned i, count;
|
|
|
|
|
|
|
|
if ([_c length] == 0)
|
|
|
|
return nil;
|
|
|
|
if ([_c isEqualToString:@"*"])
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
etags = [_c componentsSeparatedByString:@","];
|
|
|
|
count = [etags count];
|
|
|
|
ma = [NSMutableArray arrayWithCapacity:count];
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
NSString *etag;
|
|
|
|
|
|
|
|
etag = [[etags objectAtIndex:i] stringByTrimmingSpaces];
|
|
|
|
#if 0 /* this is non-sense, right? */
|
|
|
|
if ([etag hasPrefix:@"\""] && [etag hasSuffix:@"\""])
|
|
|
|
etag = [etag substringWithRange:NSMakeRange(1, [etag length] - 2)];
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (etag != nil) [ma addObject:etag];
|
|
|
|
}
|
|
|
|
return ma;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSException *)checkIfMatchCondition:(NSString *)_c inContext:(id)_ctx {
|
|
|
|
/*
|
|
|
|
Only run the request if one of the etags matches the resource etag,
|
|
|
|
usually used to ensure consistent PUTs.
|
|
|
|
*/
|
|
|
|
NSArray *etags;
|
|
|
|
NSString *etag;
|
|
|
|
|
|
|
|
if ([_c isEqualToString:@"*"])
|
|
|
|
/* to ensure that the resource exists! */
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
if ((etags = [self parseETagList:_c]) == nil)
|
|
|
|
return nil;
|
|
|
|
if ([etags count] == 0) /* no etags to check for? */
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
etag = [self davEntityTag];
|
|
|
|
if ([etag length] == 0) /* has no etag, ignore */
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
if ([etags containsObject:etag]) {
|
|
|
|
[self debugWithFormat:@"etag '%@' matches: %@", etag,
|
|
|
|
[etags componentsJoinedByString:@","]];
|
|
|
|
return nil; /* one etag matches, so continue with request */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* hack for Kontact 3.4 */
|
|
|
|
|
|
|
|
if (kontactGroupDAV) {
|
|
|
|
WEClientCapabilities *cc;
|
|
|
|
|
|
|
|
cc = [[(WOContext *)_ctx request] clientCapabilities];
|
|
|
|
if ([[cc userAgentType] isEqualToString:@"Konqueror"]) {
|
|
|
|
if ([cc majorVersion] == 3 && [cc minorVersion] == 4) {
|
|
|
|
[self logWithFormat:
|
|
|
|
@"WARNING: applying Kontact 3.4 GroupDAV hack"
|
|
|
|
@" - etag check is disabled!"
|
|
|
|
@" (can be enabled using 'ZSDisableKontact34GroupDAVHack')"];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: we might want to return the davEntityTag in the response
|
|
|
|
[self debugWithFormat:@"etag '%@' does not match: %@", etag,
|
|
|
|
[etags componentsJoinedByString:@","]];
|
|
|
|
return [NSException exceptionWithHTTPStatus:412 /* Precondition Failed */
|
|
|
|
reason:@"Precondition Failed"];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSException *)checkIfNoneMatchCondition:(NSString *)_c inContext:(id)_ctx {
|
|
|
|
/*
|
|
|
|
If one of the etags is still the same, we can ignore the request.
|
|
|
|
|
|
|
|
Can be used for PUT to ensure that the object does not exist in the store
|
|
|
|
and for GET to retrieve the content only if if the etag changed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (![_c isEqualToString:@"*"] &&
|
|
|
|
[[[_ctx request] method] isEqualToString:@"GET"]) {
|
|
|
|
NSString *etag;
|
|
|
|
NSArray *etags;
|
|
|
|
|
|
|
|
if ((etags = [self parseETagList:_c]) == nil)
|
|
|
|
return nil;
|
|
|
|
if ([etags count] == 0) /* no etags to check for? */
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
etag = [self davEntityTag];
|
|
|
|
if ([etag length] == 0) /* has no etag, ignore */
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
if ([etags containsObject:etag]) {
|
|
|
|
[self debugWithFormat:@"etag '%@' matches: %@", etag,
|
|
|
|
[etags componentsJoinedByString:@","]];
|
|
|
|
/* one etag matches, so stop the request */
|
|
|
|
return [NSException exceptionWithHTTPStatus:304 /* Not Modified */
|
|
|
|
reason:@"object was not modified"];
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if ([_c isEqualToString:@"*"])
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
if ((a = [self parseETagList:_c]) == nil)
|
|
|
|
return nil;
|
|
|
|
#else
|
|
|
|
[self logWithFormat:@"TODO: implement if-none-match for etag: '%@'", _c];
|
|
|
|
#endif
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSException *)matchesRequestConditionInContext:(id)_ctx {
|
|
|
|
NSException *error;
|
|
|
|
WORequest *rq;
|
|
|
|
NSString *c;
|
|
|
|
|
|
|
|
if ((rq = [(WOContext *)_ctx request]) == nil)
|
|
|
|
return nil; /* be tolerant - no request, no condition */
|
|
|
|
|
|
|
|
if ((c = [rq headerForKey:@"if-match"]) != nil) {
|
|
|
|
if ((error = [self checkIfMatchCondition:c inContext:_ctx]) != nil)
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if ((c = [rq headerForKey:@"if-none-match"]) != nil) {
|
|
|
|
if ((error = [self checkIfNoneMatchCondition:c inContext:_ctx]) != nil)
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2007-04-17 16:09:29 +02:00
|
|
|
/* acls */
|
|
|
|
|
2007-11-29 17:14:11 +01:00
|
|
|
/* roles required to obtain the "authorized subscriber" role */
|
|
|
|
- (NSArray *) subscriptionRoles
|
|
|
|
{
|
|
|
|
return [container subscriptionRoles];
|
|
|
|
}
|
|
|
|
|
2008-02-22 22:19:29 +01:00
|
|
|
- (BOOL) addUserInAcls: (NSString *) uid
|
|
|
|
{
|
|
|
|
BOOL result;
|
|
|
|
|
|
|
|
if ([uid length]
|
2008-12-15 23:01:52 +01:00
|
|
|
&& ![uid isEqualToString: [self ownerInContext: nil]])
|
2008-02-22 22:19:29 +01:00
|
|
|
{
|
|
|
|
[self setRoles: [self aclsForUser: uid]
|
|
|
|
forUser: uid];
|
|
|
|
if (sendACLAdvisories)
|
|
|
|
[self sendACLAdditionAdvisoryToUser: uid];
|
|
|
|
result = YES;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
result = NO;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) removeUserFromAcls: (NSString *) uid
|
|
|
|
{
|
|
|
|
BOOL result;
|
|
|
|
|
|
|
|
if ([uid length])
|
|
|
|
{
|
|
|
|
[self removeAclsForUsers: [NSArray arrayWithObject: uid]];
|
|
|
|
if (sendACLAdvisories)
|
|
|
|
[self sendACLRemovalAdvisoryToUser: uid];
|
|
|
|
result = YES;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
result = NO;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2007-05-19 02:39:52 +02:00
|
|
|
- (NSArray *) aclUsers
|
2007-04-17 16:09:29 +02:00
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *) aclsForUser: (NSString *) uid
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2007-04-25 00:45:38 +02:00
|
|
|
- (void) setRoles: (NSArray *) roles
|
2007-04-17 16:09:29 +02:00
|
|
|
forUser: (NSString *) uid
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) removeAclsForUsers: (NSArray *) users
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
}
|
|
|
|
|
2007-05-22 20:36:23 +02:00
|
|
|
- (NSString *) defaultUserID
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2007-05-30 22:18:42 +02:00
|
|
|
- (void) sendACLAdvisoryTemplate: (NSString *) template
|
|
|
|
toUser: (NSString *) uid
|
2007-05-19 02:39:52 +02:00
|
|
|
{
|
2007-05-30 22:18:42 +02:00
|
|
|
NSString *language, *pageName;
|
|
|
|
SOGoUser *user;
|
|
|
|
SOGoACLAdvisory *page;
|
|
|
|
WOApplication *app;
|
|
|
|
|
|
|
|
user = [SOGoUser userWithLogin: uid roles: nil];
|
|
|
|
language = [user language];
|
|
|
|
pageName = [NSString stringWithFormat: @"SOGoACL%@%@Advisory",
|
|
|
|
language, template];
|
|
|
|
|
|
|
|
app = [WOApplication application];
|
|
|
|
page = [app pageWithName: pageName inContext: context];
|
|
|
|
[page setACLObject: self];
|
|
|
|
[page setRecipientUID: uid];
|
|
|
|
[page send];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) sendACLAdditionAdvisoryToUser: (NSString *) uid
|
|
|
|
{
|
|
|
|
return [self sendACLAdvisoryTemplate: @"Addition"
|
|
|
|
toUser: uid];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) sendACLRemovalAdvisoryToUser: (NSString *) uid
|
|
|
|
{
|
|
|
|
return [self sendACLAdvisoryTemplate: @"Removal"
|
|
|
|
toUser: uid];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSURL *) _urlPreferringParticle: (NSString *) expected
|
|
|
|
overThisOne: (NSString *) possible
|
|
|
|
{
|
2007-06-01 23:04:56 +02:00
|
|
|
NSURL *serverURL, *url;
|
2007-05-30 22:18:42 +02:00
|
|
|
NSMutableArray *path;
|
2008-10-15 16:52:02 +02:00
|
|
|
NSString *baseURL, *urlMethod, *fullHost;
|
2008-10-16 22:15:15 +02:00
|
|
|
NSNumber *port;
|
2007-05-30 22:18:42 +02:00
|
|
|
|
|
|
|
serverURL = [context serverURL];
|
2007-06-01 23:04:56 +02:00
|
|
|
baseURL = [[self baseURLInContext: context] stringByUnescapingURL];
|
2007-05-30 22:18:42 +02:00
|
|
|
path = [NSMutableArray arrayWithArray: [baseURL componentsSeparatedByString:
|
|
|
|
@"/"]];
|
2007-06-05 17:45:10 +02:00
|
|
|
if ([baseURL hasPrefix: @"http"])
|
|
|
|
{
|
|
|
|
[path removeObjectAtIndex: 1];
|
|
|
|
[path removeObjectAtIndex: 0];
|
2007-06-05 17:57:02 +02:00
|
|
|
[path replaceObjectAtIndex: 0 withObject: @""];
|
2007-06-05 17:45:10 +02:00
|
|
|
}
|
2007-05-30 22:18:42 +02:00
|
|
|
urlMethod = [path objectAtIndex: 2];
|
|
|
|
if (![urlMethod isEqualToString: expected])
|
|
|
|
{
|
|
|
|
if ([urlMethod isEqualToString: possible])
|
|
|
|
[path replaceObjectAtIndex: 2 withObject: expected];
|
|
|
|
else
|
|
|
|
[path insertObject: expected atIndex: 2];
|
|
|
|
}
|
|
|
|
|
2008-10-16 22:15:15 +02:00
|
|
|
port = [serverURL port];
|
|
|
|
if (port)
|
|
|
|
fullHost = [NSString stringWithFormat: @"%@:%@",
|
|
|
|
[serverURL host], port];
|
|
|
|
else
|
|
|
|
fullHost = [serverURL host];
|
|
|
|
|
2007-06-01 23:04:56 +02:00
|
|
|
url = [[NSURL alloc] initWithScheme: [serverURL scheme]
|
2008-10-15 16:52:02 +02:00
|
|
|
host: fullHost
|
2007-06-01 23:04:56 +02:00
|
|
|
path: [path componentsJoinedByString: @"/"]];
|
|
|
|
[url autorelease];
|
2007-05-30 22:18:42 +02:00
|
|
|
|
2007-06-01 23:04:56 +02:00
|
|
|
return url;
|
2007-05-30 22:18:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSURL *) davURL
|
|
|
|
{
|
|
|
|
return [self _urlPreferringParticle: @"dav" overThisOne: @"so"];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSURL *) soURL
|
|
|
|
{
|
|
|
|
return [self _urlPreferringParticle: @"so" overThisOne: @"dav"];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSURL *) soURLToBaseContainerForUser: (NSString *) uid
|
|
|
|
{
|
|
|
|
NSURL *soURL, *baseSoURL;
|
|
|
|
NSArray *basePath;
|
|
|
|
NSMutableArray *newPath;
|
|
|
|
|
|
|
|
soURL = [self soURL];
|
|
|
|
basePath = [[soURL path] componentsSeparatedByString: @"/"];
|
|
|
|
newPath
|
|
|
|
= [NSMutableArray arrayWithArray:
|
|
|
|
[basePath subarrayWithRange: NSMakeRange (0, 5)]];
|
|
|
|
[newPath replaceObjectAtIndex: 3 withObject: uid];
|
|
|
|
|
|
|
|
baseSoURL = [[NSURL alloc] initWithScheme: [soURL scheme]
|
|
|
|
host: [soURL host]
|
|
|
|
path: [newPath componentsJoinedByString: @"/"]];
|
|
|
|
[baseSoURL autorelease];
|
|
|
|
|
|
|
|
return baseSoURL;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSURL *) soURLToBaseContainerForCurrentUser
|
|
|
|
{
|
|
|
|
NSString *currentLogin;
|
|
|
|
|
|
|
|
currentLogin = [[context activeUser] login];
|
|
|
|
|
|
|
|
return [self soURLToBaseContainerForUser: currentLogin];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) httpURLForAdvisoryToUser: (NSString *) uid
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) resourceURLForAdvisoryToUser: (NSString *) uid
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
|
|
|
|
return nil;
|
2007-05-19 02:39:52 +02:00
|
|
|
}
|
|
|
|
|
2008-06-23 16:23:18 +02:00
|
|
|
- (NSArray *) _languagesForLabels
|
|
|
|
{
|
|
|
|
NSMutableArray *languages;
|
|
|
|
NSArray *browserLanguages;
|
|
|
|
NSString *language;
|
|
|
|
NSUserDefaults *ud;
|
|
|
|
|
|
|
|
languages = [NSMutableArray array];
|
|
|
|
|
|
|
|
language = [[context activeUser] language];
|
|
|
|
[languages addObject: language];
|
|
|
|
browserLanguages = [[context request] browserLanguages];
|
|
|
|
[languages addObjectsFromArray: browserLanguages];
|
|
|
|
ud = [NSUserDefaults standardUserDefaults];
|
|
|
|
language = [ud stringForKey: @"SOGoDefaultLanguage"];
|
|
|
|
if (language)
|
|
|
|
[languages addObject: language];
|
|
|
|
[languages addObject: @"English"];
|
|
|
|
|
|
|
|
return languages;
|
|
|
|
}
|
|
|
|
|
2007-09-14 23:59:00 +02:00
|
|
|
- (NSString *) labelForKey: (NSString *) key
|
|
|
|
{
|
2008-06-23 16:23:18 +02:00
|
|
|
NSString *language, *label;
|
2007-09-14 23:59:00 +02:00
|
|
|
NSArray *paths;
|
2008-06-23 16:23:18 +02:00
|
|
|
NSEnumerator *languages;
|
2007-09-14 23:59:00 +02:00
|
|
|
NSBundle *bundle;
|
|
|
|
NSDictionary *strings;
|
|
|
|
|
2008-06-23 16:23:18 +02:00
|
|
|
label = nil;
|
|
|
|
|
2007-09-14 23:59:00 +02:00
|
|
|
bundle = [NSBundle bundleForClass: [self class]];
|
|
|
|
if (!bundle)
|
|
|
|
bundle = [NSBundle mainBundle];
|
2008-06-23 16:23:18 +02:00
|
|
|
languages = [[self _languagesForLabels] objectEnumerator];
|
2007-09-14 23:59:00 +02:00
|
|
|
|
2008-06-23 16:23:18 +02:00
|
|
|
while (!label && (language = [languages nextObject]))
|
2007-09-14 23:59:00 +02:00
|
|
|
{
|
2008-06-23 16:23:18 +02:00
|
|
|
paths = [bundle pathsForResourcesOfType: @"strings"
|
|
|
|
inDirectory: [NSString stringWithFormat: @"%@.lproj",
|
|
|
|
language]
|
|
|
|
forLocalization: language];
|
|
|
|
if ([paths count] > 0)
|
|
|
|
{
|
|
|
|
strings = [NSDictionary
|
|
|
|
dictionaryFromStringsFile: [paths objectAtIndex: 0]];
|
|
|
|
label = [strings objectForKey: key];
|
|
|
|
}
|
2007-09-14 23:59:00 +02:00
|
|
|
}
|
2008-06-23 16:23:18 +02:00
|
|
|
if (!label)
|
2007-09-14 23:59:00 +02:00
|
|
|
label = key;
|
|
|
|
|
|
|
|
return label;
|
|
|
|
}
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* description */
|
|
|
|
|
2008-05-03 01:08:15 +02:00
|
|
|
- (void) appendAttributesToDescription: (NSMutableString *) _ms
|
|
|
|
{
|
2006-09-08 17:14:07 +02:00
|
|
|
if (nameInContainer)
|
|
|
|
[_ms appendFormat:@" name=%@", nameInContainer];
|
|
|
|
if (container)
|
2006-06-15 21:34:10 +02:00
|
|
|
[_ms appendFormat:@" container=0x%08X/%@",
|
2006-09-08 17:14:07 +02:00
|
|
|
container, [container valueForKey:@"nameInContainer"]];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2008-05-03 01:08:15 +02:00
|
|
|
- (NSString *) description
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
NSMutableString *ms;
|
|
|
|
|
|
|
|
ms = [NSMutableString stringWithCapacity:64];
|
|
|
|
[ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
|
|
|
|
[self appendAttributesToDescription:ms];
|
|
|
|
[ms appendString:@">"];
|
2006-09-08 17:14:07 +02:00
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
return ms;
|
|
|
|
}
|
|
|
|
|
2007-11-07 16:59:01 +01:00
|
|
|
- (NSString *) loggingPrefix
|
|
|
|
{
|
|
|
|
return [NSString stringWithFormat:@"<0x%08X[%@]:%@>",
|
|
|
|
self, NSStringFromClass([self class]),
|
|
|
|
[self nameInContainer]];
|
|
|
|
}
|
|
|
|
|
2008-02-08 15:28:27 +01:00
|
|
|
- (NSArray *) davComplianceClassesInContext: (WOContext *) localContext
|
|
|
|
{
|
|
|
|
NSMutableArray *newClasses;
|
|
|
|
|
|
|
|
newClasses
|
|
|
|
= [NSMutableArray arrayWithArray:
|
|
|
|
[super davComplianceClassesInContext: localContext]];
|
|
|
|
[newClasses addObject: @"access-control"];
|
|
|
|
|
|
|
|
return newClasses;
|
|
|
|
}
|
|
|
|
|
2008-02-22 22:19:29 +01:00
|
|
|
/* dav acls */
|
|
|
|
- (NSString *) davRecordForUser: (NSString *) user
|
2008-12-23 23:39:56 +01:00
|
|
|
parameters: (NSArray *) params
|
2008-02-22 22:19:29 +01:00
|
|
|
{
|
|
|
|
NSMutableString *userRecord;
|
|
|
|
|
|
|
|
userRecord = [NSMutableString string];
|
|
|
|
|
|
|
|
[userRecord appendFormat: @"<id>%@</id>",
|
|
|
|
[user stringByEscapingXMLString]];
|
2008-12-23 23:39:56 +01:00
|
|
|
|
2009-03-02 18:45:32 +01:00
|
|
|
// Make sure to not call [SOGoUser userWithLogin...] here if nocn AND noemail
|
|
|
|
// is specified. We'll avoid generating LDAP calls by doing so.
|
2008-12-23 23:39:56 +01:00
|
|
|
if (![params containsObject: @"nocn"])
|
|
|
|
{
|
|
|
|
NSString *cn;
|
|
|
|
|
2009-03-02 18:45:32 +01:00
|
|
|
cn = [[SOGoUser userWithLogin: user roles: nil] cn];
|
2008-12-23 23:39:56 +01:00
|
|
|
if (!cn)
|
|
|
|
cn = user;
|
|
|
|
[userRecord appendFormat: @"<displayName>%@</displayName>",
|
|
|
|
[cn stringByEscapingXMLString]];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (![params containsObject: @"noemail"])
|
|
|
|
{
|
|
|
|
NSString *email;
|
|
|
|
|
2009-03-02 18:45:32 +01:00
|
|
|
email = [[[SOGoUser userWithLogin: user roles: nil]
|
|
|
|
allEmails] objectAtIndex: 0];
|
2008-12-23 23:39:56 +01:00
|
|
|
if (email)
|
|
|
|
[userRecord appendFormat: @"<email>%@</email>",
|
|
|
|
[email stringByEscapingXMLString]];
|
|
|
|
}
|
2008-02-22 22:19:29 +01:00
|
|
|
|
|
|
|
return userRecord;
|
|
|
|
}
|
|
|
|
|
2008-12-23 23:39:56 +01:00
|
|
|
- (NSString *) _davAclUserListQuery: (NSString *) theParameters
|
2008-02-22 22:19:29 +01:00
|
|
|
{
|
|
|
|
NSMutableString *userList;
|
|
|
|
NSString *defaultUserID, *currentUserID;
|
|
|
|
NSEnumerator *users;
|
2008-12-23 23:39:56 +01:00
|
|
|
NSArray *params;
|
|
|
|
|
|
|
|
if (theParameters && [theParameters length])
|
|
|
|
params = [[theParameters lowercaseString] componentsSeparatedByString: @","];
|
|
|
|
else
|
|
|
|
params = [NSArray array];
|
2008-02-22 22:19:29 +01:00
|
|
|
|
|
|
|
userList = [NSMutableString string];
|
|
|
|
|
|
|
|
defaultUserID = [self defaultUserID];
|
|
|
|
if ([defaultUserID length])
|
|
|
|
[userList appendFormat: @"<default-user><id>%@</id></default-user>",
|
|
|
|
[defaultUserID stringByEscapingXMLString]];
|
|
|
|
users = [[self aclUsers] objectEnumerator];
|
|
|
|
while ((currentUserID = [users nextObject]))
|
2008-12-23 23:39:56 +01:00
|
|
|
{
|
|
|
|
if (![currentUserID isEqualToString: defaultUserID])
|
|
|
|
{
|
|
|
|
[userList appendFormat: @"<user>%@</user>",
|
|
|
|
[self davRecordForUser: currentUserID
|
|
|
|
parameters: params]];
|
|
|
|
}
|
|
|
|
}
|
2008-02-22 22:19:29 +01:00
|
|
|
|
|
|
|
return userList;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) _davAclUserRoles: (NSString *) userName
|
|
|
|
{
|
|
|
|
NSArray *roles;
|
|
|
|
|
|
|
|
roles = [[self aclsForUser: userName] stringsWithFormat: @"<%@/>"];
|
|
|
|
|
|
|
|
return [roles componentsJoinedByString: @""];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *) _davGetRolesFromRequest: (id <DOMNode>) node
|
|
|
|
{
|
|
|
|
NSMutableArray *roles;
|
2008-05-06 01:41:24 +02:00
|
|
|
NSArray *childNodes;
|
2008-02-22 22:19:29 +01:00
|
|
|
NSString *currentRole;
|
|
|
|
unsigned int count, max;
|
|
|
|
|
|
|
|
roles = [NSMutableArray array];
|
2008-05-06 01:41:24 +02:00
|
|
|
childNodes = [self domNode: node getChildNodesByType: DOM_ELEMENT_NODE];
|
|
|
|
max = [childNodes count];
|
2008-02-22 22:19:29 +01:00
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
2008-05-06 01:41:24 +02:00
|
|
|
currentRole = [[childNodes objectAtIndex: count] localName];
|
2008-02-22 22:19:29 +01:00
|
|
|
[roles addObject: currentRole];
|
|
|
|
}
|
|
|
|
|
|
|
|
return roles;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) _davAclActionFromQuery: (id <DOMDocument>) document
|
|
|
|
{
|
|
|
|
id <DOMNode> node, userAttr;
|
|
|
|
id <DOMNamedNodeMap> attrs;
|
|
|
|
NSString *nodeName, *result, *response, *user;
|
2008-12-15 23:01:52 +01:00
|
|
|
NSArray *childNodes, *allUsers, *allRoles;
|
|
|
|
int i;
|
2008-02-22 22:19:29 +01:00
|
|
|
|
2008-05-06 01:41:24 +02:00
|
|
|
result = nil;
|
|
|
|
|
|
|
|
childNodes = [self domNode: [document documentElement]
|
|
|
|
getChildNodesByType: DOM_ELEMENT_NODE];
|
|
|
|
if ([childNodes count])
|
2008-02-22 22:19:29 +01:00
|
|
|
{
|
2008-05-06 01:41:24 +02:00
|
|
|
node = [childNodes objectAtIndex: 0];
|
|
|
|
nodeName = [node localName];
|
2008-12-23 23:39:56 +01:00
|
|
|
|
|
|
|
//
|
|
|
|
// We support parameters during the user-list REPORT.
|
|
|
|
// We do that in order to avoid looking up from the LDAP
|
|
|
|
// the CN and the EMAIL address of the users that are
|
|
|
|
// returned in the REPORT's response. That can be slow if
|
|
|
|
// the LDAP server used for authentication is slow and this
|
|
|
|
// information might not be necessary for scripting purposes.
|
|
|
|
//
|
|
|
|
// The following parameters are supported:
|
|
|
|
//
|
|
|
|
// nocn - avoid returning the CN
|
|
|
|
// noemail - avoid returning the EMAIL address
|
|
|
|
//
|
|
|
|
// Both can be specified using:
|
|
|
|
//
|
|
|
|
// params=nocn,noemail
|
|
|
|
//
|
2008-05-06 01:41:24 +02:00
|
|
|
if ([nodeName isEqualToString: @"user-list"])
|
2008-12-23 23:39:56 +01:00
|
|
|
{
|
|
|
|
result = [self _davAclUserListQuery: [[[node attributes]
|
|
|
|
namedItem: @"params"]
|
|
|
|
nodeValue]];
|
|
|
|
}
|
2008-05-06 01:41:24 +02:00
|
|
|
else if ([nodeName isEqualToString: @"roles"])
|
2008-02-22 22:19:29 +01:00
|
|
|
{
|
2008-05-06 01:41:24 +02:00
|
|
|
attrs = [node attributes];
|
|
|
|
userAttr = [attrs namedItem: @"user"];
|
|
|
|
user = [userAttr nodeValue];
|
|
|
|
if ([user length])
|
|
|
|
result = [self _davAclUserRoles: user];
|
|
|
|
}
|
|
|
|
else if ([nodeName isEqualToString: @"set-roles"])
|
|
|
|
{
|
2008-12-15 23:01:52 +01:00
|
|
|
// We support two ways of setting roles. The first one is, for example:
|
|
|
|
//
|
|
|
|
// <?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
// <acl-query xmlns="urn:inverse:params:xml:ns:inverse-dav">
|
|
|
|
// <set-roles user="alice"><PrivateViewer/></set-roles></acl-query>
|
|
|
|
//
|
|
|
|
// while the second one, for mutliple users at the same time, is:
|
|
|
|
//
|
|
|
|
// <?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
// <acl-query xmlns="urn:inverse:params:xml:ns:inverse-dav">
|
|
|
|
// <set-roles users="alice,bob,bernard"><PrivateViewer/></set-roles></acl-query>
|
|
|
|
//
|
2008-05-06 01:41:24 +02:00
|
|
|
attrs = [node attributes];
|
|
|
|
userAttr = [attrs namedItem: @"user"];
|
|
|
|
user = [userAttr nodeValue];
|
2008-12-15 23:01:52 +01:00
|
|
|
|
2008-05-06 01:41:24 +02:00
|
|
|
if ([user length])
|
2008-12-15 23:01:52 +01:00
|
|
|
allUsers = [NSArray arrayWithObject: user];
|
2008-12-23 23:39:56 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
userAttr = [attrs namedItem: @"users"];
|
|
|
|
allUsers = [[userAttr nodeValue] componentsSeparatedByString: @","];
|
|
|
|
}
|
2008-12-15 23:01:52 +01:00
|
|
|
|
|
|
|
allRoles = [self _davGetRolesFromRequest: node];
|
|
|
|
for (i = 0; i < [allUsers count]; i++)
|
2008-05-06 01:41:24 +02:00
|
|
|
{
|
2008-12-15 23:01:52 +01:00
|
|
|
[self setRoles: allRoles
|
|
|
|
forUser: [allUsers objectAtIndex: i]];
|
2008-05-06 01:41:24 +02:00
|
|
|
}
|
2008-12-15 23:01:52 +01:00
|
|
|
result = @"";
|
2008-05-06 01:41:24 +02:00
|
|
|
}
|
2008-12-15 23:01:52 +01:00
|
|
|
//
|
|
|
|
// Here again, we support two ways of adding users. The first one is, for example:
|
|
|
|
//
|
|
|
|
// <?xml version="1.0" encoding="utf-8"?>
|
|
|
|
// <acl-query xmlns="urn:inverse:params:xml:ns:inverse-dav">
|
|
|
|
// <add-user user="alice"/></acl-query>
|
|
|
|
//
|
|
|
|
// while the second one, for mutliple users at the same time, is:
|
|
|
|
//
|
|
|
|
// <?xml version="1.0" encoding="utf-8"?>
|
|
|
|
// <acl-query xmlns="urn:inverse:params:xml:ns:inverse-dav">
|
|
|
|
// <add-users users="alice,bob,bernard"/></acl-query>
|
|
|
|
//
|
2008-05-06 01:41:24 +02:00
|
|
|
else if ([nodeName isEqualToString: @"add-user"])
|
|
|
|
{
|
|
|
|
attrs = [node attributes];
|
|
|
|
userAttr = [attrs namedItem: @"user"];
|
|
|
|
user = [userAttr nodeValue];
|
|
|
|
if ([self addUserInAcls: user])
|
|
|
|
result = @"";
|
|
|
|
}
|
2008-12-15 23:01:52 +01:00
|
|
|
else if ([nodeName isEqualToString: @"add-users"])
|
|
|
|
{
|
|
|
|
attrs = [node attributes];
|
|
|
|
userAttr = [attrs namedItem: @"users"];
|
|
|
|
allUsers = [[userAttr nodeValue] componentsSeparatedByString: @","];
|
|
|
|
|
|
|
|
for (i = 0; i < [allUsers count]; i++)
|
|
|
|
{
|
|
|
|
if ([self addUserInAcls: [allUsers objectAtIndex: i]])
|
|
|
|
result = @"";
|
2008-12-23 23:39:56 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
result = nil;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-12-15 23:01:52 +01:00
|
|
|
}
|
|
|
|
//
|
|
|
|
// See the comment for add-user / add-users
|
|
|
|
//
|
2008-05-06 01:41:24 +02:00
|
|
|
else if ([nodeName isEqualToString: @"remove-user"])
|
|
|
|
{
|
|
|
|
attrs = [node attributes];
|
|
|
|
userAttr = [attrs namedItem: @"user"];
|
|
|
|
user = [userAttr nodeValue];
|
|
|
|
if ([self removeUserFromAcls: user])
|
|
|
|
result = @"";
|
2008-02-22 22:19:29 +01:00
|
|
|
}
|
2008-12-15 23:01:52 +01:00
|
|
|
else if ([nodeName isEqualToString: @"remove-users"])
|
|
|
|
{
|
|
|
|
attrs = [node attributes];
|
|
|
|
userAttr = [attrs namedItem: @"users"];
|
|
|
|
|
|
|
|
allUsers = [[userAttr nodeValue] componentsSeparatedByString: @","];
|
|
|
|
|
|
|
|
for (i = 0; i < [allUsers count]; i++)
|
|
|
|
{
|
|
|
|
if ([self removeUserFromAcls: [allUsers objectAtIndex: i]])
|
|
|
|
result = @"";
|
2008-12-23 23:39:56 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
result = nil;
|
|
|
|
break;
|
|
|
|
}
|
2008-12-15 23:01:52 +01:00
|
|
|
}
|
|
|
|
}
|
2008-02-22 22:19:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
{
|
|
|
|
if ([result length])
|
|
|
|
response = [NSString stringWithFormat: @"<%@>%@</%@>",
|
|
|
|
nodeName, result, nodeName];
|
|
|
|
else
|
|
|
|
response = @"";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
response = nil;
|
|
|
|
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) davAclQuery: (WOContext *) queryContext
|
|
|
|
{
|
|
|
|
WOResponse *r;
|
|
|
|
id <DOMDocument> document;
|
|
|
|
NSString *content;
|
|
|
|
|
|
|
|
r = [queryContext response];
|
|
|
|
[r setContentEncoding: NSUTF8StringEncoding];
|
|
|
|
[r setHeader: @"no-cache" forKey: @"pragma"];
|
|
|
|
[r setHeader: @"no-cache" forKey: @"cache-control"];
|
|
|
|
|
|
|
|
document = [[context request] contentAsDOMDocument];
|
|
|
|
content = [self _davAclActionFromQuery: document];
|
|
|
|
if (content)
|
|
|
|
{
|
|
|
|
if ([content length])
|
|
|
|
{
|
2008-03-17 19:47:06 +01:00
|
|
|
[r setStatus: 207];
|
2008-04-24 03:14:21 +02:00
|
|
|
[r setHeader: @"application/xml; charset=\"utf-8\""
|
|
|
|
forKey: @"content-type"];
|
|
|
|
[r appendContentString:
|
|
|
|
@"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"];
|
2008-02-22 22:19:29 +01:00
|
|
|
[r appendContentString: content];
|
|
|
|
}
|
2008-03-17 19:47:06 +01:00
|
|
|
else
|
|
|
|
[r setStatus: 204];
|
2008-02-22 22:19:29 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
[r setStatus: 400];
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2008-04-22 01:03:48 +02:00
|
|
|
- (NSException *) davSetProperties: (NSDictionary *) setProps
|
|
|
|
removePropertiesNamed: (NSDictionary *) removedProps
|
|
|
|
inContext: (WOContext *) localContext
|
|
|
|
{
|
|
|
|
NSString *currentProp;
|
|
|
|
NSException *exception;
|
|
|
|
NSEnumerator *properties;
|
|
|
|
id currentValue;
|
2008-04-22 16:50:40 +02:00
|
|
|
SEL methodSel;
|
2008-04-22 01:03:48 +02:00
|
|
|
|
2008-04-22 16:50:40 +02:00
|
|
|
properties = [[setProps allKeys] objectEnumerator];
|
|
|
|
exception = nil;
|
|
|
|
while (!exception
|
|
|
|
&& (currentProp = [properties nextObject]))
|
2008-04-22 01:03:48 +02:00
|
|
|
{
|
2008-04-22 16:50:40 +02:00
|
|
|
methodSel = NSSelectorFromString ([currentProp davSetterName]);
|
|
|
|
if ([self respondsToSelector: methodSel])
|
2008-04-22 01:03:48 +02:00
|
|
|
{
|
|
|
|
currentValue = [setProps objectForKey: currentProp];
|
2008-04-22 16:50:40 +02:00
|
|
|
exception = [self performSelector: methodSel
|
2009-06-02 19:59:06 +02:00
|
|
|
withObject: currentValue];
|
2008-04-22 01:03:48 +02:00
|
|
|
}
|
2008-04-22 16:50:40 +02:00
|
|
|
else
|
|
|
|
exception
|
|
|
|
= [NSException exceptionWithHTTPStatus: 404
|
|
|
|
reason: [NSString stringWithFormat:
|
|
|
|
@"Property '%@' cannot be set.",
|
|
|
|
currentProp]];
|
2008-04-22 01:03:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return exception;
|
|
|
|
}
|
|
|
|
|
2008-05-03 01:08:15 +02:00
|
|
|
- (SOGoWebDAVValue *) davSupportedReportSet
|
2008-04-24 03:14:21 +02:00
|
|
|
{
|
2008-05-03 01:08:15 +02:00
|
|
|
NSDictionary *currentValue;
|
2008-04-24 03:14:21 +02:00
|
|
|
NSEnumerator *reportKeys;
|
|
|
|
NSMutableArray *reportSet;
|
2008-05-03 01:08:15 +02:00
|
|
|
NSString *currentKey;
|
2008-04-24 03:14:21 +02:00
|
|
|
|
|
|
|
reportSet = [NSMutableArray array];
|
|
|
|
|
|
|
|
reportKeys = [[reportMap allKeys] objectEnumerator];
|
|
|
|
while ((currentKey = [reportKeys nextObject]))
|
|
|
|
if ([self _reportSelector: currentKey])
|
|
|
|
{
|
2008-05-03 01:08:15 +02:00
|
|
|
currentValue = [currentKey asDavInvocation];
|
|
|
|
[reportSet addObject: davElementWithContent(@"report",
|
|
|
|
@"DAV:",
|
|
|
|
currentValue)];
|
2008-04-24 03:14:21 +02:00
|
|
|
}
|
|
|
|
|
2008-05-03 01:08:15 +02:00
|
|
|
return [davElementWithContent (@"supported-report-set", @"DAV:", reportSet)
|
|
|
|
asWebDAVValue];
|
2008-04-24 03:14:21 +02:00
|
|
|
}
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
@end /* SOGoObject */
|
2008-05-03 01:08:15 +02:00
|
|
|
|
|
|
|
@implementation SOGoObject (SOGoDomHelpers)
|
|
|
|
|
|
|
|
- (NSArray *) domNode: (id <DOMNode>) node
|
|
|
|
getChildNodesByType: (DOMNodeType ) type
|
|
|
|
{
|
|
|
|
NSMutableArray *nodes;
|
|
|
|
id <DOMNode> currentChild;
|
|
|
|
|
|
|
|
nodes = [NSMutableArray array];
|
|
|
|
|
|
|
|
currentChild = [node firstChild];
|
|
|
|
while (currentChild)
|
|
|
|
{
|
|
|
|
if ([currentChild nodeType] == type)
|
|
|
|
[nodes addObject: currentChild];
|
|
|
|
currentChild = [currentChild nextSibling];
|
|
|
|
}
|
|
|
|
|
|
|
|
return nodes;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|