Monotone-Parent: 04adb21a2d66943bc4fbda82a60f41695303ccfb

Monotone-Revision: 771d7ebe867ac081c62c4c31e2177ddc7ac487a9

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2010-01-14T23:19:19
Monotone-Branch: ca.inverse.sogo
This commit is contained in:
Wolfgang Sourdeau
2010-01-14 23:19:19 +00:00
parent cbf4bc2519
commit aaded8c774
71 changed files with 2856 additions and 1407 deletions

View File

@@ -1,5 +1,76 @@
2010-01-14 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/Appointments/SOGoAppointmentFolder.m
(+webdavAclManager): fixed "read-free-busy" namespace declaration.
* SoObjects/SOGo/SOGoObject.m (-davPrincipalCollectionSet): when
used with iCal 4, we provide a "DAV" header with the proper
compliance classes, since iCal 4 doesn't want to use OPTIONS.
* Tests/test-webdav.py (WebDAVTest.testExpandProperty): new method
to test the new "expand-propery" code in SOGo.
* SoObjects/SOGo/SOGoFolder.m (-davExpandProperty): new method to
handle the "expand-property" REPORT.
* SoObjects/Appointments/SOGoCalendarProxy.m:
adapted module to new code for handling chosen calendars
delegation.
* Main/SOGo+DAV.[hm): new category module for handling dav methods
pertaining to /SOGo/dav/ as the principal-collection-set. Now
implements 3 methods that were previously found in SOGoUserFolder
categories.
* SoObjects/Appointments/SOGoUser+Appointments.[hm]: new category
module.
* SoObjects/Appointments/SOGoAppointmentInboxFolder.[hm]: new
class module that implements the basic "schedule-inbox"
collection mechanisms, enough to let iCal work properly. To be
completed for full invitation handling.
* SoObjects/SOGo/SOGoObject.m (-lookupObjectAtDAVUrl:): new method
that enables DAV reports to lookup objects based on the urls
passed in their parameters.
(-davURLAsString): simplified method by making it recurse from its
top parent.
(-setRoles:forUsers:): treat a list of users at once.
(-davPrincipalMatch): removed method since it really pertains to
/SOGo/dav, which the correct principal-collection-set.
* SoObjects/SOGo/SOGoParentFolder.m (-initSubcribedSubFolders):
enable the super user to see any user's subscriptions. This
enables a new set of tests to work properly in super user mode.
(-appendSubscribedSources): consequently to the above, the
references must be based on the owner user rather than the current
user.
* SoObjects/SOGo/SOGoUser.m: removed commented out methods.
* SoObjects/SOGo/SOGoUserSettings.m (-setProxiedCalendars)
(-setCalendarProxyUsers:withWriteAccess:):
(-setCalendarProxySubscriptionUsers:withWriteAccess:): new method
for handling caldav-proxy related settings.
* SoObjects/SOGo/SOGoWebDAVAclManager.m (_registerChild:of:): only
emits a warning of the specified parent cannot be found, instead
of crashing.
* SoObjects/SOGo/WOResponse+SOGo.m (-prepareDAVResponse): new
method with common code for DAV responses generated from SOGo.
* Tests/webdavlib.py (WebDAVPROPPATCH): new class implementing the
"PROPPATCH" method.
(WebDAVExpandProperty): new class implementing the
"expand-property" REPORT.
(_WD_XMLTreeElement.appendSubtree): new method that enables the
addition of a tree of objects from a dictionary passed as
parameter.
* Tests/test-ical.py: new test script for tesing iCal-related
behaviours.
* Tools/SOGoToolBackup.m (-extractUserPreferences:intoRecord:):
fixed compilation warning.

View File

@@ -19,6 +19,7 @@ all::
$(SOGOD)_OBJC_FILES += \
sogod.m \
SOGo.m \
SOGo+DAV.m \
SOGoProductLoader.m \
build.m

39
Main/SOGo+DAV.h Normal file
View File

@@ -0,0 +1,39 @@
/* SOGo+DAV.h - this file is part of SOGo
*
* Copyright (C) 2010 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 SOGO_DAV_H
#define SOGO_DAV_H
@class WOContext;
@class WOResponse;
#import "SOGo.h"
@interface SOGo (SOGoWebDAVExtensions)
- (WOResponse *) davPrincipalMatch: (WOContext *) localContext;
- (WOResponse *) davPrincipalPropertySearch: (WOContext *) localContext;
- (WOResponse *) davPrincipalSearchPropertySet: (WOContext *) localContext;
@end
#endif /* SOGO_DAV_H */

513
Main/SOGo+DAV.m Normal file
View File

@@ -0,0 +1,513 @@
/* SOGo+DAV.m - this file is part of SOGo
*
* Copyright (C) 2010 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/NSDictionary.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGExtensions/NSObject+Logs.h>
#import <DOM/DOMDocument.h>
#import <DOM/DOMNode.h>
#import <DOM/DOMProtocols.h>
#import <SaxObjC/XMLNamespaces.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSObject+DAV.h>
#import <SOGo/NSObject+Utilities.h>
#import <SOGo/NSString+DAV.h>
#import <SOGo/WOResponse+SOGo.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoUser.h>
#import "SOGo+DAV.h"
@implementation SOGo (SOGoWebDAVExtensions)
- (WOResponse *) davPrincipalSearchPropertySet: (WOContext *) localContext
{
static NSDictionary *davResponse = nil;
NSMutableArray *propertySet;
NSDictionary *property, *prop, *description;
NSArray *currentValue, *properties, *descriptions, *namespaces;
int count, max;
WOResponse *r;
if (!davResponse)
{
properties = [NSArray arrayWithObjects:
@"calendar-user-type",
@"calendar-user-address-set",
@"displayname",
@"first-name",
@"last-name",
@"email-address-set",
nil];
descriptions
= [properties resultsOfSelector: @selector (asDAVPropertyDescription)];
namespaces = [NSArray arrayWithObjects:
XMLNS_CALDAV,
XMLNS_CALDAV,
XMLNS_WEBDAV,
XMLNS_CalendarServerOrg,
XMLNS_CalendarServerOrg,
XMLNS_CalendarServerOrg,
nil];
max = [properties count];
propertySet = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
property = davElement([properties objectAtIndex: count],
[namespaces objectAtIndex: count]);
prop = davElementWithContent(@"prop", @"DAV:",
property);
description
= davElementWithContent(@"description", @"DAV:",
[descriptions objectAtIndex: count]);
currentValue = [NSArray arrayWithObjects: prop, description, nil];
[propertySet
addObject: davElementWithContent(@"principal-search-property",
@"DAV:",
currentValue)];
}
davResponse = davElementWithContent (@"principal-search-property-set",
@"DAV:",
propertySet);
[davResponse retain];
}
r = [localContext response];
[r prepareDAVResponse];
[r appendContentString: [davResponse asWebDavStringWithNamespaces: nil]];
return r;
}
#warning all REPORT method should be standardized...
- (NSDictionary *) _formalizePrincipalMatchResponse: (NSArray *) hrefs
{
NSDictionary *multiStatus;
NSEnumerator *hrefList;
NSString *currentHref;
NSMutableArray *responses;
NSArray *responseElements;
responses = [NSMutableArray array];
hrefList = [hrefs objectEnumerator];
while ((currentHref = [hrefList nextObject]))
{
responseElements
= [NSArray arrayWithObjects: davElementWithContent (@"href",
XMLNS_WEBDAV,
currentHref),
davElementWithContent (@"status", XMLNS_WEBDAV,
@"HTTP/1.1 200 OK"),
nil];
[responses addObject: davElementWithContent (@"response", XMLNS_WEBDAV,
responseElements)];
}
multiStatus = davElementWithContent (@"multistatus", XMLNS_WEBDAV,
responses);
return multiStatus;
}
- (NSDictionary *) _handlePrincipalMatchSelfInContext: (WOContext *) localContext
{
NSString *davURL, *userLogin;
NSArray *principalURL;
davURL = [self davURLAsString];
userLogin = [[localContext activeUser] login];
principalURL
= [NSArray arrayWithObject: [NSString stringWithFormat: @"%@%@/", davURL,
userLogin]];
return [self _formalizePrincipalMatchResponse: principalURL];
}
- (void) _fillArrayWithPrincipalsOwnedBySelf: (NSMutableArray *) hrefs
inContext: (WOContext *) localContext
{
NSArray *roles;
roles = [[localContext activeUser] rolesForObject: self
inContext: localContext];
if ([roles containsObject: SoRole_Owner])
[hrefs addObject: [self davURLAsString]];
}
- (NSDictionary *)
_handlePrincipalMatchPrincipalProperty: (id <DOMElement>) child
inContext: (WOContext *) localContext
{
NSMutableArray *hrefs;
NSDictionary *response;
hrefs = [NSMutableArray array];
[self _fillArrayWithPrincipalsOwnedBySelf: hrefs
inContext: localContext];
response = [self _formalizePrincipalMatchResponse: hrefs];
return response;
}
- (NSDictionary *) _handlePrincipalMatchReport: (id <DOMDocument>) document
inContext: (WOContext *) localContext
{
NSDictionary *response;
id <DOMElement> documentElement, queryChild;
NSArray *children;
NSString *queryTag, *error;
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 _handlePrincipalMatchSelfInContext: localContext];
else if ([queryTag isEqualToString: @"principal-property"])
response = [self _handlePrincipalMatchPrincipalProperty: queryChild
inContext: localContext];
else
{
error = (@"Query element must be either '{DAV:}principal-property'"
@" or '{DAV:}self'");
response = [NSException exceptionWithHTTPStatus: 400
reason: error];
}
}
else
{
error = (@"Query must have one element: '{DAV:}principal-property'"
@" or '{DAV:}self'");
response = [NSException exceptionWithHTTPStatus: 400
reason: error];
}
return response;
}
- (WOResponse *) davPrincipalMatch: (WOContext *) localContext
{
WOResponse *r;
id <DOMDocument> document;
NSDictionary *xmlResponse;
r = [localContext response];
document = [[localContext request] contentAsDOMDocument];
xmlResponse = [self _handlePrincipalMatchReport: document
inContext: localContext];
if ([xmlResponse isKindOfClass: [NSException class]])
r = (WOResponse *) xmlResponse;
else
{
[r prepareDAVResponse];
[r appendContentString: [xmlResponse asWebDavStringWithNamespaces: nil]];
}
return r;
}
- (void) _fillMatches: (NSMutableDictionary *) matches
fromElement: (NSObject <DOMElement> *) searchElement
{
NSObject <DOMNodeList> *list;
NSObject <DOMNode> *valueNode;
NSArray *elements;
NSString *property, *match;
list = [searchElement getElementsByTagName: @"prop"];
if ([list length])
{
elements = [self domNode: [list objectAtIndex: 0]
getChildNodesByType: DOM_ELEMENT_NODE];
if ([elements count])
{
valueNode = [elements objectAtIndex: 0];
property = [NSString stringWithFormat: @"{%@}%@",
[valueNode namespaceURI],
[valueNode nodeName]];
}
}
list = [searchElement getElementsByTagName: @"match"];
if ([list length])
{
valueNode = [[list objectAtIndex: 0] firstChild];
match = [valueNode nodeValue];
}
[matches setObject: match forKey: property];
}
- (void) _fillProperties: (NSMutableArray *) properties
fromElement: (NSObject <DOMElement> *) propElement
{
NSEnumerator *elements;
NSObject <DOMElement> *propNode;
NSString *property;
elements = [[self domNode: propElement
getChildNodesByType: DOM_ELEMENT_NODE]
objectEnumerator];
while ((propNode = [elements nextObject]))
{
property = [NSString stringWithFormat: @"{%@}%@",
[propNode namespaceURI],
[propNode nodeName]];
[properties addObject: property];
}
}
- (void) _fillPrincipalMatches: (NSMutableDictionary *) matches
andProperties: (NSMutableArray *) properties
fromElement: (NSObject <DOMElement> *) documentElement
{
NSEnumerator *children;
NSObject <DOMElement> *currentElement;
NSString *tag;
children = [[self domNode: documentElement
getChildNodesByType: DOM_ELEMENT_NODE]
objectEnumerator];
while ((currentElement = [children nextObject]))
{
tag = [currentElement tagName];
if ([tag isEqualToString: @"property-search"])
[self _fillMatches: matches fromElement: currentElement];
else if ([tag isEqualToString: @"prop"])
[self _fillProperties: properties fromElement: currentElement];
else
[self errorWithFormat: @"principal-property-search: unknown tag '%@'",
tag];
}
}
#warning this is a bit ugly, as usual
- (void) _fillCollections: (NSMutableArray *) collections
withCalendarHomeSetMatching: (NSString *) value
inContext: (WOContext *) localContext
{
SOGoUserFolder *collection;
NSRange substringRange;
substringRange = [value rangeOfString: @"/SOGo/dav/"];
value = [value substringFromIndex: NSMaxRange (substringRange)];
substringRange = [value rangeOfString: @"/Calendar"];
value = [value substringToIndex: substringRange.location];
collection = [[SOGoUser userWithLogin: value]
homeFolderInContext: localContext];
if (collection)
[collections addObject: collection];
}
- (NSMutableArray *) _firstPrincipalCollectionsWhere: (NSString *) key
matches: (NSString *) value
inContext: (WOContext *) localContext
{
NSMutableArray *collections;
collections = [NSMutableArray array];
if ([key
isEqualToString: @"{urn:ietf:params:xml:ns:caldav}calendar-home-set"])
[self _fillCollections: collections withCalendarHomeSetMatching: value
inContext: localContext];
else
[self errorWithFormat: @"principal-property-search: unhandled key '%@'",
key];
return collections;
}
#warning unused stub
- (BOOL) collectionDavKey: (NSString *) key
matches: (NSString *) value
{
return YES;
}
- (void) _principalCollections: (NSMutableArray **) baseCollections
where: (NSString *) key
matches: (NSString *) value
inContext: (WOContext *) localContext
{
SOGoUserFolder *currentCollection;
unsigned int count, max;
if (!*baseCollections)
*baseCollections = [self _firstPrincipalCollectionsWhere: key
matches: value
inContext: localContext];
else
{
max = [*baseCollections count];
for (count = max; count > 0; count--)
{
currentCollection = [*baseCollections objectAtIndex: count - 1];
if (![currentCollection collectionDavKey: key matches: value])
[*baseCollections removeObjectAtIndex: count - 1];
}
}
}
- (NSArray *) _principalCollectionsMatching: (NSDictionary *) matches
inContext: (WOContext *) localContext
{
NSMutableArray *collections;
NSEnumerator *allKeys;
NSString *currentKey;
collections = nil;
allKeys = [[matches allKeys] objectEnumerator];
while ((currentKey = [allKeys nextObject]))
[self _principalCollections: &collections
where: currentKey
matches: [matches objectForKey: currentKey]
inContext: localContext];
return collections;
}
/* <D:principal-property-search xmlns:D="DAV:">
<D:property-search>
<D:prop>
<D:displayname/>
</D:prop>
<D:match>doE</D:match>
</D:property-search>
<D:property-search>
<D:prop xmlns:B="http://www.example.com/ns/">
<B:title/>
</D:prop>
<D:match>Sales</D:match>
</D:property-search>
<D:prop xmlns:B="http://www.example.com/ns/">
<D:displayname/>
<B:department/>
<B:phone/>
<B:office/>
<B:salary/>
</D:prop>
</D:principal-property-search> */
- (void) _appendProperties: (NSArray *) properties
ofCollection: (SOGoUserFolder *) collection
toResponses: (NSMutableArray *) responses
{
unsigned int count, max;
SEL methodSel;
NSString *currentProperty;
id currentValue;
NSMutableArray *response, *props;
NSDictionary *keyTuple;
response = [NSMutableArray array];
[response addObject: davElementWithContent (@"href", XMLNS_WEBDAV,
[collection davURLAsString])];
props = [NSMutableArray array];
max = [properties count];
for (count = 0; count < max; count++)
{
currentProperty = [properties objectAtIndex: count];
methodSel = SOGoSelectorForPropertyGetter (currentProperty);
if (methodSel && [collection respondsToSelector: methodSel])
{
currentValue = [collection performSelector: methodSel];
#warning evil eVIL EVIl!
if ([currentValue isKindOfClass: [NSArray class]])
{
currentValue = [currentValue objectAtIndex: 0];
currentValue
= davElementWithContent ([currentValue objectAtIndex: 0],
[currentValue objectAtIndex: 1],
[currentValue objectAtIndex: 3]);
}
keyTuple = [currentProperty asWebDAVTuple];
[props addObject: davElementWithContent ([keyTuple objectForKey: @"method"],
[keyTuple objectForKey: @"ns"],
currentValue)];
}
}
[response addObject: davElementWithContent (@"propstat", XMLNS_WEBDAV,
davElementWithContent
(@"prop", XMLNS_WEBDAV,
props))];
[responses addObject: davElementWithContent (@"response", XMLNS_WEBDAV,
response)];
}
- (void) _appendProperties: (NSArray *) properties
ofCollections: (NSArray *) collections
toResponse: (WOResponse *) response
{
NSDictionary *mStatus;
NSMutableArray *responses;
unsigned int count, max;
max = [collections count];
responses = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
[self _appendProperties: properties
ofCollection: [collections objectAtIndex: count]
toResponses: responses];
mStatus = davElementWithContent (@"multistatus", XMLNS_WEBDAV, responses);
[response appendContentString: [mStatus asWebDavStringWithNamespaces: nil]];
}
/* rfc3744, should be moved into SOGoUserFolder */
- (WOResponse *) davPrincipalPropertySearch: (WOContext *) localContext
{
NSObject <DOMDocument> *document;
NSObject <DOMElement> *documentElement;
NSArray *collections;
NSMutableDictionary *matches;
NSMutableArray *properties;
WOResponse *r;
document = [[localContext request] contentAsDOMDocument];
documentElement = [document documentElement];
matches = [NSMutableDictionary dictionary];
properties = [NSMutableArray array];
[self _fillPrincipalMatches: matches andProperties: properties
fromElement: documentElement];
collections = [self _principalCollectionsMatching: matches
inContext: localContext];
r = [localContext response];
[r prepareDAVResponse];
[self _appendProperties: properties ofCollections: collections
toResponse: r];
return r;
}
@end

View File

@@ -54,6 +54,7 @@
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/SOGoWebAuthenticator.h>
#import <SOGo/WORequest+SOGo.h>
#import <SOGo/NSObject+DAV.h>
#import "build.h"
#import "SOGoProductLoader.h"
@@ -61,6 +62,8 @@
#import "SOGo.h"
#warning might be useful to have a SOGoObject-derived proxy class for \
handling requests and avoid duplicating methods
@implementation SOGo
static unsigned int vMemSizeLimit;
@@ -107,10 +110,8 @@ static BOOL debugLeaks;
/* to allow public access to all contained objects (subkeys) */
[sInfo setDefaultAccess: @"allow"];
basicRoles = [NSArray arrayWithObjects: SoRole_Authenticated,
SOGoRole_FreeBusy, nil];
/* require Authenticated role for View and WebDAV */
basicRoles = [NSArray arrayWithObject: SoRole_Authenticated];
[sInfo declareRoles: basicRoles asDefaultForPermission: SoPerm_View];
[sInfo declareRoles: basicRoles asDefaultForPermission: SoPerm_WebDAVAccess];
@@ -268,15 +269,6 @@ static BOOL debugLeaks;
/* name lookup */
- (BOOL) isUserName: (NSString *) _key
inContext: (id) _ctx
{
if ([_key length] < 1)
return NO;
return YES;
}
- (id) lookupUser: (NSString *) _key
inContext: (id)_ctx
{
@@ -311,16 +303,29 @@ static BOOL debugLeaks;
acquire: (BOOL) _flag
{
id obj;
SOGoSystemDefaults *sd;
WORequest *request;
BOOL isDAVRequest;
/* put locale info into the context in case it's not there */
[self _setupLocaleInContext:_ctx];
sd = [SOGoSystemDefaults sharedSystemDefaults];
if ([sd isWebAccessEnabled] || [[_ctx request] isSoWebDAVRequest])
request = [_ctx request];
isDAVRequest = [request isSoWebDAVRequest];
if (isDAVRequest
|| [[SOGoSystemDefaults sharedSystemDefaults] isWebAccessEnabled])
{
/* first check attributes directly bound to the application */
obj = [super lookupName:_key inContext:_ctx acquire:_flag];
if (isDAVRequest)
{
if ([[request method] isEqualToString: @"REPORT"])
obj = [self davReportInvocationForKey: _key];
else
obj = nil;
}
else
{
/* first check attributes directly bound to the application */
obj = [super lookupName:_key inContext:_ctx acquire:_flag];
}
if (!obj)
{
/*
@@ -331,11 +336,8 @@ static BOOL debugLeaks;
"GET" if no method was provided in the query path.
*/
if (![_key isEqualToString:@"favicon.ico"])
{
if ([self isUserName: _key inContext: _ctx])
obj = [self lookupUser: _key inContext: _ctx];
}
if ([_key length] > 0 && ![_key isEqualToString:@"favicon.ico"])
obj = [self lookupUser: _key inContext: _ctx];
}
}
else
@@ -589,15 +591,21 @@ static BOOL debugLeaks;
{
NSURL *davURL;
NSString *davURLAsString;
davURL = [self davURL];
WORequest *request;
/* we know that GNUstep returns a "/" suffix for the absoluteString but not
for the path method. Therefore we add one. */
if (useRelativeURLs)
davURLAsString = [NSString stringWithFormat: @"%@/", [davURL path]];
{
request = [[self context] request];
davURLAsString = [NSString stringWithFormat: @"/%@/dav/",
[request applicationName]];
}
else
davURLAsString = [davURL absoluteString];
{
davURL = [self davURL];
davURLAsString = [davURL absoluteString];
}
return davURLAsString;
}

4
NEWS
View File

@@ -10,6 +10,10 @@
values more easily
- added sensible default configuration values
- updated ckeditor to version 3.1
- added support for iCal 4 delegation
- added support for letting the user choose which calendars should be shared
with iCal delegation
- added fixes for bugs in GNUstep 1.19.3 (NSURL)
1.1-20091028 (1.1.0)
--------------------

View File

@@ -1162,7 +1162,7 @@ Index: sope-mime/NGImap4/NGImap4Connection.m
}
return self;
}
@@ -100,11 +102,13 @@
@@ -100,14 +102,16 @@
return self->creationTime;
}
@@ -1179,7 +1179,11 @@ Index: sope-mime/NGImap4/NGImap4Connection.m
+ return [self->subfolders objectForKey:[_url absoluteString]];
}
- (void)flushFolderHierarchyCache {
[self->subfolders release]; self->subfolders = nil;
- [self->subfolders release]; self->subfolders = nil;
+ [self->subfolders release]; self->subfolders = [NSMutableDictionary new];
[self->urlToRights release]; self->urlToRights = nil;
}
@@ -152,7 +156,6 @@
ASSIGN(self->cachedUIDs, nil);
}
@@ -1341,7 +1345,7 @@ Index: sope-mime/NGImap4/NGImap4Connection.m
if (![[result valueForKey:@"result"] boolValue]) {
return [self errorForResult:result
text:@"Failed to change flags of IMAP4 message"];
@@ -737,14 +770,17 @@
@@ -737,20 +770,21 @@
- (BOOL)doesMailboxExistAtURL:(NSURL *)_url {
NSString *folderName;
@@ -1361,9 +1365,15 @@ Index: sope-mime/NGImap4/NGImap4Connection.m
+
+ result = [[caches objectAtIndex: count] objectForKey:@"list"];
p = [_url path];
#if __APPLE__
-#if __APPLE__
/* normalized results already have the / in front on libFoundation?! */
@@ -760,11 +796,11 @@
if ([p hasPrefix:@"/"])
p = [p substringFromIndex:1];
-#endif
if ([p hasSuffix:@"/"])
p = [p substringToIndex:[p length]-1];
return ([(NSDictionary *)result objectForKey:p] != nil) ? YES : NO;
@@ -760,11 +794,11 @@
// TODO: we should probably just fetch the whole hierarchy?
folderName = [self imap4FolderNameForURL:_url];
@@ -1380,7 +1390,7 @@ Index: sope-mime/NGImap4/NGImap4Connection.m
}
- (id)infoForMailboxAtURL:(NSURL *)_url {
@@ -789,7 +825,8 @@
@@ -789,7 +823,8 @@
/* construct path */
newPath = [self imap4FolderNameForURL:_url];
@@ -2389,7 +2399,14 @@ Index: sope-mime/NGImap4/ChangeLog
===================================================================
--- sope-mime/NGImap4/ChangeLog (revision 1664)
+++ sope-mime/NGImap4/ChangeLog (working copy)
@@ -1,3 +1,92 @@
@@ -1,3 +1,99 @@
+2010-01-14 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * NGImap4Connection.m (-doesMailboxExistAtURL:): mailbox paths can
+ start with '/' on non-Apple platforms.
+ (-flushFolderHierarchyCache): reassign a new dictionary to
+ self->subfolders to avoid disappearing folders.
+
+2010-01-05 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * NGImap4ResponseParser.m (_parseUntaggedResponse): now accepts
@@ -4993,6 +5010,20 @@ Index: sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.m
return self->contentHandler;
}
Index: sope-xml/SaxObjC/XMLNamespaces.h
===================================================================
--- sope-xml/SaxObjC/XMLNamespaces.h (revision 1664)
+++ sope-xml/SaxObjC/XMLNamespaces.h (working copy)
@@ -292,6 +292,9 @@
#ifndef XMLNS_AppleCalServer
# define XMLNS_AppleCalServer @"http://apple.com/ns/calendarserver/"
#endif
+#ifndef XMLNS_CalendarServerOrg
+# define XMLNS_CalendarServerOrg @"http://calendarserver.org/ns/"
+#endif
#ifndef XMLNS_AppleCalApp
# define XMLNS_AppleCalApp @"com.apple.ical:"
#endif
Index: sope-appserver/mod_ngobjweb/GNUmakefile
===================================================================
--- sope-appserver/mod_ngobjweb/GNUmakefile (revision 1664)
@@ -6447,7 +6478,18 @@ Index: sope-appserver/NGObjWeb/ChangeLog
===================================================================
--- sope-appserver/NGObjWeb/ChangeLog (revision 1664)
+++ sope-appserver/NGObjWeb/ChangeLog (working copy)
@@ -1,3 +1,97 @@
@@ -1,3 +1,108 @@
+2010-01-14 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * SoObjects/SoObject.m (-isFolderish): now a real category method,
+ defaulting to NO.
+
+ * WebDAV/SoWebDAVRenderer.m (-renderSearchResultEntry:...): take
+ the potential ending slash of the request to keep or remove the
+ ending slash of the hrefs to the returned objects, in order to
+ avoid confusing iCal with otherwise standard urls to DAV
+ collections.
+
+2009-12-22 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * WOWatchDogApplicationMain.m (_listeningAddress): read "WOPort"
@@ -6557,6 +6599,15 @@ Index: sope-appserver/NGObjWeb/DAVPropMap.plist
/* CardDAV */
"{urn:ietf:params:xml:ns:carddav}addressbook-home-set" = davAddressbookHomeSet;
@@ -168,6 +169,8 @@
"{http://calendarserver.org/ns/}dropbox-home-URL" = davDropboxHomeURL;
"{http://calendarserver.org/ns/}notifications-URL" = davNotificationsURL;
"{http://calendarserver.org/ns/}getctag" = davCollectionTag;
+ "{http://calendarserver.org/ns/}calendar-proxy-read-for" = davCalendarProxyReadFor;
+ "{http://calendarserver.org/ns/}calendar-proxy-write-for" = davCalendarProxyWriteFor;
/* Apple extensions */
"{http://apple.com/ns/ical/}calendar-color" = davCalendarColor;
Index: sope-appserver/NGObjWeb/WebDAV/SoObjectResultEntry.m
===================================================================
--- sope-appserver/NGObjWeb/WebDAV/SoObjectResultEntry.m (revision 1664)
@@ -6616,11 +6667,32 @@ Index: sope-appserver/NGObjWeb/WebDAV/SoObjectResultEntry.m
if ([_key isEqualToString:@"{DAV:}status"])
return nil;
@@ -102,6 +135,12 @@
}
}
+/* SoObject */
+- (BOOL)isFolderish
+{
+ return [self->object isFolderish];
+}
+
/* description */
- (NSString *)description {
Index: sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m
===================================================================
--- sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m (revision 1664)
+++ sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m (working copy)
@@ -49,6 +49,8 @@
@@ -25,6 +25,7 @@
#include "SoObject+SoDAV.h"
#include "EOFetchSpecification+SoDAV.h"
#include "NSException+HTTP.h"
+#include <NGObjWeb/SoObject.h>
#include <NGObjWeb/WOContext.h>
#include <NGObjWeb/WOResponse.h>
#include <NGObjWeb/WORequest.h>
@@ -49,6 +50,8 @@
#define XMLNS_INTTASK \
@"{http://schemas.microsoft.com/mapi/id/{00062003-0000-0000-C000-000000000046}/}"
@@ -6629,7 +6701,7 @@ Index: sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m
@interface SoWebDAVRenderer(Privates)
- (BOOL)renderStatusResult:(id)_object withDefaultStatus:(int)_defStatus
inContext:(WOContext *)_ctx;
@@ -79,6 +81,8 @@
@@ -79,6 +82,8 @@
if ((debugOn = [ud boolForKey:@"SoRendererDebugEnabled"]))
NSLog(@"enabled debugging in SoWebDAVRenderer (SoRendererDebugEnabled)");
@@ -6638,7 +6710,7 @@ Index: sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m
}
+ (id)sharedRenderer {
@@ -616,16 +620,19 @@
@@ -616,16 +621,19 @@
[r appendContentString:s];
}
else {
@@ -6666,7 +6738,19 @@ Index: sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m
if (formatOutput) [r appendContentCharacter:'\n'];
}
}
@@ -694,8 +701,13 @@
@@ -646,8 +654,9 @@
NSString *key;
id href = nil;
id stat = nil;
- BOOL isBrief;
-
+ BOOL isBrief, hasSlash;
+
+ hasSlash = [[[_ctx request] uri] hasSuffix: @"/"];
r = [_ctx response];
isBrief = [[[_ctx request] headerForKey:@"brief"] hasPrefix:@"t"] ? YES : NO;
@@ -694,8 +703,13 @@
}
/* tidy href */
@@ -6682,6 +6766,30 @@ Index: sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m
/* tidy status */
stat = [self tidyStatus:stat];
}
@@ -703,7 +717,22 @@
href = [baseURL stringValue];
stat = @"HTTP/1.1 200 OK";
}
-
+
+ /* make the presence of the href slash correspond to the request slash */
+ if (hasSlash) {
+ /* megahack: we consider entry to be the base entry if it's an
+ NSDictionary */
+ if (![href hasSuffix: @"/"]
+ && ([entry isFolderish]
+ || [entry isKindOfClass: [NSDictionary class]])) {
+ href = [href stringByAppendingString: @"/"];
+ }
+ }
+ else {
+ if ([href hasSuffix: @"/"])
+ href = [href substringToIndex: [href length] - 2];
+ }
+
if (debugOn) {
[self debugWithFormat:@" status: %@", stat];
[self debugWithFormat:@" href: %@", href];
Index: sope-appserver/NGObjWeb/WODirectAction.m
===================================================================
--- sope-appserver/NGObjWeb/WODirectAction.m (revision 1664)
@@ -6863,11 +6971,33 @@ Index: sope-appserver/NGObjWeb/WOMessage.m
}
return nil;
}
Index: sope-appserver/NGObjWeb/SoObjects/SoObject.h
===================================================================
--- sope-appserver/NGObjWeb/SoObjects/SoObject.h (revision 1664)
+++ sope-appserver/NGObjWeb/SoObjects/SoObject.h (working copy)
@@ -59,6 +59,8 @@
- (NSString *)defaultMethodNameInContext:(id)_ctx;
- (id)lookupDefaultMethod;
+- (BOOL)isFolderish;
+
/* binding (returns self by default [unbound objects]) */
- (id)bindToObject:(id)_object inContext:(id)_ctx;
Index: sope-appserver/NGObjWeb/SoObjects/SoObject.m
===================================================================
--- sope-appserver/NGObjWeb/SoObjects/SoObject.m (revision 1664)
+++ sope-appserver/NGObjWeb/SoObjects/SoObject.m (working copy)
@@ -39,22 +39,34 @@
@@ -30,31 +30,39 @@
#include <NGObjWeb/WORequest.h>
#include "common.h"
-@interface NSObject(Folders)
-- (BOOL)isFolderish;
-@end
-
@implementation NSObject(SoObject)
static int debugLookup = -1;
static int debugBaseURL = -1;
static int useRelativeURLs = -1;
@@ -6908,7 +7038,32 @@ Index: sope-appserver/NGObjWeb/SoObjects/SoObject.m
}
/* classes */
@@ -318,56 +330,61 @@
@@ -241,6 +249,11 @@
return pathArray;
}
+- (BOOL) isFolderish
+{
+ return NO;
+}
+
- (NSString *)baseURLInContext:(id)_ctx {
NSString *baseURL;
id parent;
@@ -284,10 +297,8 @@
/* add a trailing slash for folders */
if (![baseURL hasSuffix:@"/"]) {
- if ([self respondsToSelector:@selector(isFolderish)]) {
- if ([self isFolderish])
- baseURL = [baseURL stringByAppendingString:@"/"];
- }
+ if ([self isFolderish])
+ baseURL = [baseURL stringByAppendingString:@"/"];
}
return baseURL;
@@ -318,56 +329,61 @@
rq = [_ctx request];
ms = [[NSMutableString alloc] initWithCapacity:128];
@@ -7724,3 +7879,16 @@ Index: sope-appserver/NGObjWeb/WOCoreApplication.m
if ([woport isKindOfClass:[NSNumber class]])
return woport;
woport = [woport stringValue];
Index: sope-appserver/NGObjWeb/NGHttp/NGHttpRequest.m
===================================================================
--- sope-appserver/NGObjWeb/NGHttp/NGHttpRequest.m (revision 1664)
+++ sope-appserver/NGObjWeb/NGHttp/NGHttpRequest.m (working copy)
@@ -59,6 +59,8 @@
/* RFC 3253 (DeltaV) */
@"REPORT",
@"VERSION-CONTROL",
+ /* RFC 3744 (WebDAV ACL) */
+ @"ACL",
/* RFC 4791 (CalDAV) */
@"MKCALENDAR",
/* http://ietfreport.isoc.org/idref/draft-daboo-carddav/ (CardDAV) */

View File

@@ -24,9 +24,11 @@ Appointments_OBJC_FILES = \
SOGoAppointmentOccurence.m \
SOGoTaskOccurence.m \
SOGoAppointmentFolder.m \
SOGoAppointmentInboxFolder.m \
SOGoWebAppointmentFolder.m \
SOGoAppointmentFolders.m \
SOGoFreeBusyObject.m \
SOGoUser+Appointments.m \
SOGoUserFolder+Appointments.m \
\
SOGoCalendarProxy.m \

View File

@@ -67,12 +67,6 @@
- (NSString *) calendarColor;
- (void) setCalendarColor: (NSString *) newColor;
- (NSString *) syncTag;
- (void) setSyncTag: (NSString *) newSyncTag;
- (BOOL) synchronizeCalendar;
- (void) setSynchronizeCalendar: (BOOL) new;
/* selection */
- (NSArray *) calendarUIDs;
@@ -144,13 +138,24 @@
- (BOOL) showCalendarTasks;
- (void) setShowCalendarTasks: (BOOL) new;
- (NSArray *) proxySubscribersWithWriteAccess: (BOOL) hasWriteAccess;
- (NSException *) setProxySubscribers: (NSArray *) newSubscribers
withWriteAccess: (BOOL) hasWriteAccess;
- (BOOL) isProxied;
- (void) setIsProxied: (BOOL) isProxied;
- (NSString *) syncTag;
- (void) setSyncTag: (NSString *) newSyncTag;
- (BOOL) synchronizeCalendar;
- (void) setSynchronizeCalendar: (BOOL) new;
- (BOOL) importComponent: (iCalEntityObject *) event;
- (int) importCalendar: (iCalCalendar *) calendar;
/* caldav proxy */
- (void) adjustProxyRolesForUsers: (NSArray *) proxyUsers
remove: (BOOL) remove
forWriteAccess: (BOOL) write;
@end
#endif /* __Appointments_SOGoAppointmentFolder_H__ */

View File

@@ -67,6 +67,7 @@
#import <SOGo/SOGoWebDAVAclManager.h>
#import <SOGo/SOGoWebDAVValue.h>
#import <SOGo/WORequest+SOGo.h>
#import <SOGo/WOResponse+SOGo.h>
#import "iCalEntityObject+SOGo.h"
#import "iCalPerson+SOGo.h"
@@ -106,16 +107,16 @@ static NSNumber *sharedYes = nil;
aclManager = [SOGoWebDAVAclManager new];
[aclManager registerDAVPermission: davElement (@"read", XMLNS_WEBDAV)
abstract: YES
withEquivalent: SoPerm_WebDAVAccess
asChildOf: davElement (@"all", XMLNS_WEBDAV)];
abstract: YES
withEquivalent: SoPerm_WebDAVAccess
asChildOf: davElement (@"all", XMLNS_WEBDAV)];
[aclManager registerDAVPermission: davElement (@"read-current-user-privilege-set", XMLNS_WEBDAV)
abstract: YES
withEquivalent: SoPerm_WebDAVAccess
asChildOf: davElement (@"read", XMLNS_WEBDAV)];
[aclManager registerDAVPermission: davElement (@"read-free-busy", XMLNS_WEBDAV)
abstract: YES
withEquivalent: SoPerm_WebDAVAccess
asChildOf: davElement (@"read", XMLNS_WEBDAV)];
[aclManager registerDAVPermission: davElement (@"read-free-busy", XMLNS_CALDAV)
abstract: NO
withEquivalent: SoPerm_AccessContentsInformation
withEquivalent: SOGoCalendarPerm_ReadFreeBusy
asChildOf: davElement (@"read", XMLNS_WEBDAV)];
[aclManager registerDAVPermission: davElement (@"write", XMLNS_WEBDAV)
abstract: YES
@@ -1668,31 +1669,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
return nil;
}
- (SOGoWebDAVValue *) davCalendarFreeBusySet
{
NSEnumerator *subFolders;
SOGoAppointmentFolder *currentFolder;
NSMutableArray *response;
SOGoWebDAVValue *responseValue;
response = [NSMutableArray array];
subFolders = [[container subFolders] objectEnumerator];
while ((currentFolder = [subFolders nextObject]))
[response
addObject: davElementWithContent (@"href", XMLNS_WEBDAV,
[currentFolder davURLAsString])];
responseValue = [davElementWithContent (@"calendar-free-busy-set", XMLNS_CALDAV, response)
asWebDAVValue];
return responseValue;
}
/* This method is ignored but we must return a success value. */
- (NSException *) setDavCalendarFreeBusySet: (NSString *) newFreeBusySet
{
return nil;
}
- (void) _appendComponentProperties: (NSDictionary *) properties
matchingFilters: (NSArray *) filters
toResponse: (WOResponse *) response
@@ -1713,9 +1689,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
if ([currentField length])
[fields addObjectUniquely: currentField];
baseURL = [self davURLAsString];
#warning review this when fixing http://www.scalableogo.org/bugs/view.php?id=276
if (![baseURL hasSuffix: @"/"])
baseURL = [NSString stringWithFormat: @"%@/", baseURL];
propertiesArray = [[properties allKeys] asPointersOfObjects];
propertiesCount = [properties count];
@@ -1775,12 +1748,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
DOMElement *documentElement, *propElement;
r = [context response];
[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 prepareDAVResponse];
[r appendContentString: @"<D:multistatus xmlns:D=\"DAV:\""
@" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">"];
@@ -1987,12 +1955,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
DOMElement *documentElement, *propElement;
r = [context response];
[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 prepareDAVResponse];
[r appendContentString: @"<D:multistatus xmlns:D=\"DAV:\""
@" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">"];
@@ -2370,8 +2333,8 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
login = [[context activeUser] login];
if ([login isEqualToString: [self ownerInContext: self]])
{
[colType addObject: [NSArray arrayWithObjects: @"schedule-inbox",
XMLNS_CALDAV, nil]];
// [colType addObject: [NSArray arrayWithObjects: @"schedule-inbox",
// XMLNS_CALDAV, nil]];
[colType addObject: [NSArray arrayWithObjects: @"schedule-outbox",
XMLNS_CALDAV, nil]];
}
@@ -2919,128 +2882,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
return folders;
}
// - (id) lookupGroupFolderForUIDs: (NSArray *) _uids
// inContext: (id)_ctx
// {
// SOGoCustomGroupFolder *folder;
// if (_uids == nil)
// return nil;
// folder = [[SOGoCustomGroupFolder alloc] initWithUIDs:_uids inContainer:self];
// return [folder autorelease];
// }
// - (id) lookupGroupCalendarFolderForUIDs: (NSArray *) _uids
// inContext: (id) _ctx
// {
// SOGoCustomGroupFolder *folder;
// if ((folder = [self lookupGroupFolderForUIDs:_uids inContext:_ctx]) == nil)
// return nil;
// folder = [folder lookupName:@"Calendar" inContext:_ctx acquire:NO];
// if (![folder isNotNull])
// return nil;
// if ([folder isKindOfClass:[NSException class]]) {
// [self debugWithFormat:@"Note: could not lookup 'Calendar' in folder: %@",
// folder];
// return nil;
// }
// return folder;
// }
/* bulk fetches */
// #warning We only support ONE calendar per user at this time
// - (BOOL) _appendSubscribedFolders: (NSDictionary *) subscribedFolders
// toFolderList: (NSMutableArray *) calendarFolders
// {
// NSEnumerator *keys;
// NSString *currentKey;
// NSMutableDictionary *currentCalendar;
// BOOL firstShouldBeActive;
// unsigned int count;
// firstShouldBeActive = YES;
// keys = [[subscribedFolders allKeys] objectEnumerator];
// currentKey = [keys nextObject];
// count = 1;
// while (currentKey)
// {
// currentCalendar = [NSMutableDictionary new];
// [currentCalendar autorelease];
// [currentCalendar
// setDictionary: [subscribedFolders objectForKey: currentKey]];
// [currentCalendar setObject: currentKey forKey: @"folder"];
// [calendarFolders addObject: currentCalendar];
// if ([[currentCalendar objectForKey: @"active"] boolValue])
// firstShouldBeActive = NO;
// count++;
// currentKey = [keys nextObject];
// }
// return firstShouldBeActive;
// }
// - (NSArray *) calendarFolders
// {
// NSMutableDictionary *userCalendar, *calendarDict;
// NSMutableArray *calendarFolders;
// SOGoUser *calendarUser;
// BOOL firstActive;
// calendarFolders = [NSMutableArray new];
// [calendarFolders autorelease];
// calendarUser = [SOGoUser userWithLogin: [self ownerInContext: context]];
// userCalendar = [NSMutableDictionary new];
// [userCalendar autorelease];
// [userCalendar setObject: @"/" forKey: @"folder"];
// [userCalendar setObject: @"Calendar" forKey: @"displayName"];
// [calendarFolders addObject: userCalendar];
// calendarDict = [[calendarUser userSettings] objectForKey: @"Calendar"];
// firstActive = [[calendarDict objectForKey: @"activateUserFolder"] boolValue];
// firstActive = ([self _appendSubscribedFolders:
// [calendarDict objectForKey: @"SubscribedFolders"]
// toFolderList: calendarFolders]
// || firstActive);
// [userCalendar setObject: [NSNumber numberWithBool: firstActive]
// forKey: @"active"];
// return calendarFolders;
// }
// - (NSArray *) fetchContentObjectNames
// {
// NSMutableArray *objectNames;
// NSArray *records;
// NSCalendarDate *today, *startDate, *endDate;
// #warning this should be user-configurable
// objectNames = [NSMutableArray array];
// today = [[NSCalendarDate calendarDate] beginOfDay];
// [today setTimeZone: timeZone];
// startDate = [today dateByAddingYears: 0 months: 0 days: -1
// hours: 0 minutes: 0 seconds: 0];
// endDate = [startDate dateByAddingYears: 0 months: 0 days: 2
// hours: 0 minutes: 0 seconds: 0];
// records = [self fetchFields: [NSArray arrayWithObject: @"c_name"]
// from: startDate to: endDate
// component: @"vevent"];
// [objectNames addObjectsFromArray: [records valueForKey: @"c_name"]];
// records = [self fetchFields: [NSArray arrayWithObject: @"c_name"]
// from: startDate to: endDate
// component: @"vtodo"];
// [objectNames addObjectsFromArray: [records valueForKey: @"c_name"]];
// return objectNames;
// }
/* folder type */
- (NSString *) folderType
@@ -3065,122 +2906,45 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
return (![inactiveFolders containsObject: nameInContainer]);
}
- (NSArray *) requiredProxyRolesWithWriteAccess: (BOOL) hasWriteAccess
- (BOOL) isProxied
{
static NSArray *writeAccessRoles = nil;
static NSArray *readAccessRoles = nil;
NSArray *proxiedCalendars;
SOGoUser *ownerUser;
if (!writeAccessRoles)
{
writeAccessRoles = [NSArray arrayWithObjects:
SOGoCalendarRole_ConfidentialModifier,
SOGoRole_ObjectCreator,
SOGoRole_ObjectEraser,
SOGoCalendarRole_PrivateModifier,
SOGoCalendarRole_PublicModifier,
nil];
[writeAccessRoles retain];
}
ownerUser = [SOGoUser userWithLogin: [self ownerInContext: nil]];
proxiedCalendars = [[ownerUser userSettings] proxiedCalendars];
if (!readAccessRoles)
{
readAccessRoles = [NSArray arrayWithObjects:
SOGoCalendarRole_ConfidentialViewer,
SOGoCalendarRole_PrivateViewer,
SOGoCalendarRole_PublicViewer,
nil];
[readAccessRoles retain];
}
return (hasWriteAccess) ? writeAccessRoles : readAccessRoles;
return [proxiedCalendars containsObject: [self realNameInContainer]];
}
- (BOOL) _user: (NSString *) user
isProxyWithWriteAccess: (BOOL) hasWriteAccess
- (void) setIsProxied: (BOOL) isProxied
{
NSArray *userRoles, *reqRoles;
BOOL isProxy;
NSMutableArray *proxiedCalendars;
NSArray *subscriptionUsers;
SOGoUser *ownerUser;
SOGoUserSettings *us;
if ([self userIsSubscriber: user])
{
userRoles = [[self aclsForUser: user]
sortedArrayUsingSelector: @selector (compare:)];
reqRoles = [self requiredProxyRolesWithWriteAccess: hasWriteAccess];
isProxy = [reqRoles isEqualToArray: userRoles];
}
ownerUser = [SOGoUser userWithLogin: [self ownerInContext: nil]];
us = [ownerUser userSettings];
proxiedCalendars = [[us proxiedCalendars] mutableCopy];
if (isProxied)
[proxiedCalendars addObjectUniquely: [self realNameInContainer]];
else
isProxy = NO;
[proxiedCalendars removeObject: [self realNameInContainer]];
return isProxy;
}
[us setProxiedCalendars: proxiedCalendars];
- (NSArray *) proxySubscribersWithWriteAccess: (BOOL) hasWriteAccess
{
NSMutableArray *subscribers;
NSEnumerator *aclUsers;
NSString *currentUser, *defaultUser;
subscriptionUsers = [us calendarProxyUsersWithWriteAccess: YES];
[self adjustProxyRolesForUsers: subscriptionUsers
remove: !isProxied
forWriteAccess: YES];
subscriptionUsers = [us calendarProxyUsersWithWriteAccess: NO];
[self adjustProxyRolesForUsers: subscriptionUsers
remove: !isProxied
forWriteAccess: NO];
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;
[us synchronize];
[proxiedCalendars autorelease];
}
- (BOOL) importComponent: (iCalEntityObject *) event
@@ -3224,4 +2988,74 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
return imported;
}
/* acls */
- (NSArray *) aclsForUser: (NSString *) uid
forObjectAtPath: (NSArray *) objectPathArray
{
NSMutableArray *aclsForUser;
NSArray *superAcls;
superAcls = [super aclsForUser: uid forObjectAtPath: objectPathArray];
if ([uid isEqualToString: [self defaultUserID]])
{
if (superAcls)
{
aclsForUser = [superAcls mutableCopy];
[aclsForUser autorelease];
}
else
aclsForUser = [NSMutableArray array];
[aclsForUser addObject: SoRole_Authenticated];
}
else
aclsForUser = (NSMutableArray *) superAcls;
return aclsForUser;
}
- (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;
}
- (void) adjustProxyRolesForUsers: (NSArray *) proxyUsers
remove: (BOOL) remove
forWriteAccess: (BOOL) write
{
NSArray *roles;
if (remove)
[self removeAclsForUsers: proxyUsers];
else
{
roles = [self requiredProxyRolesWithWriteAccess: write];
[self setRoles: roles forUsers: proxyUsers];
}
}
@end /* SOGoAppointmentFolder */

View File

@@ -29,9 +29,16 @@
@interface SOGoAppointmentFolders : SOGoParentFolder
- (NSArray *) proxyFoldersWithWriteAccess: (BOOL) hasWriteAccess;
- (NSArray *) webCalendarIds;
- (void) adjustProxyRolesForUsers: (NSArray *) proxyUsers
remove: (BOOL) remove
forWriteAccess: (BOOL) write;
- (void) adjustProxySubscriptionsForUsers: (NSArray *) proxyUsers
remove: (BOOL) remove
forWriteAccess: (BOOL) write;
@end
#endif /* SOGOAPPOINTMENTFOLDERS_H */

View File

@@ -33,18 +33,22 @@
#import <GDLAccess/EOAdaptorChannel.h>
#import <DOM/DOMProtocols.h>
#import <SaxObjC/XMLNamespaces.h>
#import <SOGo/WORequest+SOGo.h>
#import <SOGo/NSObject+DAV.h>
#import <SOGo/SOGoWebDAVValue.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoParentFolder.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/SOGoWebDAVValue.h>
#import <SOGo/SOGoWebDAVAclManager.h>
#import "SOGoAppointmentFolder.h"
#import "SOGoAppointmentInboxFolder.h"
#import "SOGoWebAppointmentFolder.h"
#import "SOGoUser+Appointments.h"
#import "SOGoAppointmentFolders.h"
@@ -73,17 +77,19 @@
- (NSArray *) toManyRelationshipKeys
{
NSMutableArray *keys;
NSEnumerator *sortedSubFolders;
SOGoGCSFolder *currentFolder;
NSString *login;
NSMutableArray *keys;
SOGoUser *ownerUser;
login = [[context activeUser] login];
if ([[context request] isICal])
{
login = [[context activeUser] login];
keys = [NSMutableArray array];
if ([owner isEqualToString: login])
{
[keys addObject: @"inbox"];
sortedSubFolders = [[self subFolders] objectEnumerator];
while ((currentFolder = [sortedSubFolders nextObject]))
{
@@ -93,7 +99,11 @@
}
}
else
[keys addObject: @"personal"];
{
ownerUser = [SOGoUser userWithLogin: owner];
keys = (NSMutableArray *) [[ownerUser userSettings]
proxiedCalendars];
}
}
else
keys = (NSMutableArray *) [super toManyRelationshipKeys];
@@ -101,6 +111,28 @@
return keys;
}
- (id) lookupName: (NSString *) name
inContext: (WOContext *) lookupContext
acquire: (BOOL) acquire
{
id obj;
NSString *login;
if ([name isEqualToString: @"inbox"])
{
login = [[context activeUser] login];
if ([owner isEqualToString: login])
obj = [SOGoAppointmentInboxFolder objectWithName: name
inContainer: self];
else
obj = nil;
}
else
obj = [super lookupName: name inContext: lookupContext acquire: NO];
return obj;
}
- (NSString *) _fetchPropertyWithName: (NSString *) propertyName
inArray: (NSArray *) section
{
@@ -245,37 +277,6 @@
return componentSet;
}
- (NSArray *) proxyFoldersWithWriteAccess: (BOOL) hasWriteAccess
{
NSMutableArray *proxyFolders;
NSArray *proxySubscribers;
NSEnumerator *folders;
SOGoAppointmentFolder *currentFolder;
NSString *folderOwner, *currentUser;
proxyFolders = [NSMutableArray array];
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;
}
- (NSArray *) webCalendarIds
{
SOGoUserSettings *us;
@@ -371,4 +372,48 @@
return aclManager;
}
- (void) adjustProxyRolesForUsers: (NSArray *) proxyUsers
remove: (BOOL) remove
forWriteAccess: (BOOL) write
{
NSArray *calendars;
SOGoUser *ownerUser;
SOGoAppointmentFolder *folder;
int count, max;
ownerUser = [SOGoUser userWithLogin: owner];
calendars = [[ownerUser userSettings] proxiedCalendars];
max = [calendars count];
for (count = 0; count < max; count++)
{
folder = [self lookupName: [calendars objectAtIndex: count]
inContext: context
acquire: NO];
[folder adjustProxyRolesForUsers: proxyUsers
remove: remove
forWriteAccess: write];
}
}
- (void) adjustProxySubscriptionsForUsers: (NSArray *) proxyUsers
remove: (BOOL) remove
forWriteAccess: (BOOL) write
{
int count, max;
SOGoUser *proxyUser;
max = [proxyUsers count];
for (count = 0; count < max; count++)
{
proxyUser = [SOGoUser userWithLogin: [proxyUsers objectAtIndex: count]];
if (proxyUser)
[proxyUser adjustProxySubscriptionToUser: owner
remove: remove
forWriteAccess: write];
else
[self warnWithFormat: @"(%@) user '%@' is invalid (ignored)",
NSStringFromSelector (_cmd), [proxyUser login]];
}
}
@end

View File

@@ -0,0 +1,43 @@
/* SOGoAppointmentInboxFolder.h - this file is part of SOGo
*
* Copyright (C) 2010 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 SOGOAPPOINTMENTINBOXFOLDER_H
#define SOGOAPPOINTMENTINBOXFOLDER_H
#import "SOGoAppointmentFolder.h"
@class NSArray;
@class NSException;
@class NSString;
@class SOGoWebDAVValue;
@interface SOGoAppointmentInboxFolder : SOGoAppointmentFolder
- (NSString *) davCollectionTag;
- (NSArray *) davResourceType;
- (SOGoWebDAVValue *) davCalendarFreeBusySet;
- (NSException *) setDavCalendarFreeBusySet: (NSString *) newFreeBusySet;
@end
#endif /* SOGOAPPOINTMENTINBOXFOLDER_H */

View File

@@ -0,0 +1,84 @@
/* SOGoAppointmentInboxFolder.m - this file is part of SOGo
*
* Copyright (C) 2010 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/NSException.h>
#import <Foundation/NSString.h>
#import <SaxObjC/XMLNamespaces.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <SOGo/NSObject+DAV.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoWebDAVValue.h>
#import "SOGoAppointmentFolders.h"
#import "SOGoAppointmentInboxFolder.h"
@implementation SOGoAppointmentInboxFolder
- (NSString *) davCollectionTag
{
return @"-1";
}
- (NSArray *) davResourceType
{
NSMutableArray *colType;
NSString *login;
colType = [NSMutableArray arrayWithCapacity: 10];
[colType addObject: @"collection"];
login = [[context activeUser] login];
if ([login isEqualToString: [self ownerInContext: self]])
[colType addObject: [NSArray arrayWithObjects: @"schedule-inbox",
XMLNS_CALDAV, nil]];
return colType;
}
- (SOGoWebDAVValue *) davCalendarFreeBusySet
{
NSEnumerator *subFolders;
SOGoAppointmentFolder *currentFolder;
NSMutableArray *response;
SOGoWebDAVValue *responseValue;
response = [NSMutableArray array];
subFolders = [[container subFolders] objectEnumerator];
while ((currentFolder = [subFolders nextObject]))
[response
addObject: davElementWithContent (@"href", XMLNS_WEBDAV,
[currentFolder davURLAsString])];
responseValue = [davElementWithContent (@"calendar-free-busy-set", XMLNS_CALDAV, response)
asWebDAVValue];
return responseValue;
}
/* This method is ignored but we must return a success value. */
- (NSException *) setDavCalendarFreeBusySet: (NSString *) newFreeBusySet
{
return nil;
}
@end

View File

@@ -23,17 +23,19 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSString.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/WORequest.h>
#import <SaxObjC/XMLNamespaces.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserSettings.h>
#import "SOGoAppointmentFolders.h"
#import "SOGoAppointmentFolder.h"
#import "SOGoCalendarProxy.h"
@implementation SOGoCalendarProxy
#define XMLNS_CALENDARSERVER @"http://calendarserver.org/ns/"
- (id) init
{
if ((self = [super init]))
@@ -61,7 +63,7 @@
else
proxyType = @"calendar-proxy-read";
[rType addObject: [NSArray arrayWithObjects: proxyType,
XMLNS_CALENDARSERVER, nil]];
XMLNS_CalendarServerOrg, nil]];
return rType;
}
@@ -69,24 +71,24 @@
- (NSArray *) davGroupMemberSet
{
NSMutableArray *members;
NSEnumerator *subscribers;
NSArray *member;
NSArray *proxyUsers, *member;
SOGoUser *ownerUser;
SOGoAppointmentFolder *folder;
NSString *subscriber;
NSString *appName, *proxyUser;
int count, max;
members = [NSMutableArray array];
appName = [[context request] applicationName];
ownerUser = [SOGoUser userWithLogin: [self ownerInContext: context]
roles: nil];
folder = [ownerUser personalCalendarFolderInContext: context];
subscribers = [[folder proxySubscribersWithWriteAccess: hasWriteAccess]
objectEnumerator];
while ((subscriber = [subscribers nextObject]))
ownerUser = [SOGoUser userWithLogin: [self ownerInContext: context]];
proxyUsers = [[ownerUser userSettings]
calendarProxyUsersWithWriteAccess: hasWriteAccess];
max = [proxyUsers count];
members = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
member = [NSArray arrayWithObjects: @"href", @"DAV:", @"D",
[NSString stringWithFormat: @"/SOGo/dav/%@/",
subscriber],
proxyUser = [proxyUsers objectAtIndex: count];
member = [NSArray arrayWithObjects: @"href", XMLNS_WEBDAV, @"D",
[NSString stringWithFormat: @"/%@/dav/%@/",
appName, proxyUser],
nil];
[members addObject: member];
}
@@ -94,11 +96,6 @@
return members;
}
- (NSString *) davGroupMembership
{
return nil;
}
- (NSString *) _parseSubscriber: (NSString *) memberSet
until: (int) length
{
@@ -143,14 +140,49 @@
- (NSException *) setDavGroupMemberSet: (NSString *) memberSet
{
SOGoUser *ownerUser;
SOGoAppointmentFolder *folder;
SOGoUserSettings *us;
NSMutableArray *addedUsers, *removedUsers;
NSArray *oldProxyUsers, *newProxyUsers;
NSString *login;
SOGoAppointmentFolders *folders;
ownerUser = [SOGoUser userWithLogin: [self ownerInContext: context]
roles: nil];
folder = [ownerUser personalCalendarFolderInContext: context];
login = [self ownerInContext: context];
ownerUser = [SOGoUser userWithLogin: login roles: nil];
us = [ownerUser userSettings];
oldProxyUsers = [us calendarProxyUsersWithWriteAccess: hasWriteAccess];
if (!oldProxyUsers)
oldProxyUsers = [NSMutableArray array];
newProxyUsers = [self _parseSubscribers: memberSet];
if (!newProxyUsers)
newProxyUsers = [NSMutableArray array];
[us setCalendarProxyUsers: newProxyUsers
withWriteAccess: hasWriteAccess];
return [folder setProxySubscribers: [self _parseSubscribers: memberSet]
withWriteAccess: hasWriteAccess];
folders = [container lookupName: @"Calendar" inContext: context
acquire: NO];
addedUsers = [newProxyUsers mutableCopy];
[addedUsers removeObjectsInArray: oldProxyUsers];
[folders adjustProxyRolesForUsers: addedUsers
remove: NO
forWriteAccess: hasWriteAccess];
[folders adjustProxySubscriptionsForUsers: addedUsers
remove: NO
forWriteAccess: hasWriteAccess];
[addedUsers autorelease];
removedUsers = [oldProxyUsers mutableCopy];
[removedUsers removeObjectsInArray: newProxyUsers];
[folders adjustProxyRolesForUsers: removedUsers
remove: YES
forWriteAccess: hasWriteAccess];
[folders adjustProxySubscriptionsForUsers: removedUsers
remove: YES
forWriteAccess: hasWriteAccess];
[removedUsers autorelease];
[us synchronize];
return nil;
}
@end

View File

@@ -0,0 +1,36 @@
/* SOGoUser+Appointments.h - this file is part of SOGo
*
* Copyright (C) 2010 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 SOGOUSER_APPOINTMENTS_H
#define SOGOUSER_APPOINTMENTS_H
#import <SOGo/SOGoUser.h>
@interface SOGoUser (SOGoCalDAVSupport)
- (void) adjustProxySubscriptionToUser: (NSString *) ownerUser
remove: (BOOL) remove
forWriteAccess: (BOOL) write;
@end
#endif /* SOGOUSER_APPOINTMENTS_H */

View File

@@ -0,0 +1,74 @@
/* SOGoUser+Appointments.m - this file is part of SOGo
*
* Copyright (C) 2010 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 <SOGo/NSArray+Utilities.h>
#import <SOGo/SOGoUserSettings.h>
#import "SOGoUser+Appointments.h"
@implementation SOGoUser (SOGoCalDAVSupport)
- (void) adjustProxySubscriptionToUser: (NSString *) ownerUser
remove: (BOOL) remove
forWriteAccess: (BOOL) write
{
SOGoUserSettings *us;
NSMutableArray *subscriptions;
us = [self userSettings];
/* first, we want to ensure the subscription does not appear in the
opposite list... */
if (!remove)
{
subscriptions
= [[us calendarProxySubscriptionUsersWithWriteAccess: !write]
mutableCopy];
[subscriptions autorelease];
if ([subscriptions containsObject: ownerUser])
{
[subscriptions removeObject: ownerUser];
[us setCalendarProxySubscriptionUsers: subscriptions
withWriteAccess: !write];
}
}
subscriptions
= [[us calendarProxySubscriptionUsersWithWriteAccess: write]
mutableCopy];
[subscriptions autorelease];
if (remove)
[subscriptions removeObject: ownerUser];
else
{
if (!subscriptions)
subscriptions = [NSMutableArray array];
[subscriptions addObjectUniquely: ownerUser];
}
[us setCalendarProxySubscriptionUsers: subscriptions
withWriteAccess: write];
[us synchronize];
}
@end

View File

@@ -32,10 +32,7 @@
- (NSArray *) davCalendarUserAddressSet;
- (NSArray *) davCalendarHomeSet;
- (NSArray *) davCalendarScheduleInboxURL;
- (NSArray *) davCalendarScheduleOutboxURL;
- (NSArray *) davDropboxHomeURL;
- (NSArray *) davNotificationsURL;
@end

View File

@@ -28,13 +28,17 @@
#import <NGObjWeb/WOResponse.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+misc.h>
#import <DOM/DOMProtocols.h>
#import <SaxObjC/XMLNamespaces.h>
#import <SOGo/SOGoGCSFolder.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSObject+DAV.h>
#import <SOGo/NSString+DAV.h>
#import <SOGo/NSObject+Utilities.h>
#import <SOGo/SOGoGCSFolder.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/WOResponse+SOGo.h>
#import <SOGo/WORequest+SOGo.h>
#import "SOGoAppointmentFolders.h"
@@ -95,417 +99,141 @@
return [NSArray arrayWithObject: tag];
}
- (NSArray *) _davPersonalCalendarURL
- (NSArray *) _davSpecialCalendarURLWithName: (NSString *) name
{
SOGoAppointmentFolders *parent;
NSArray *tag;
NSString *parentURL;
NSArray *tag, *response;
NSString *parentURL, *login;
parent = [self privateCalendars: @"Calendar" inContext: context];
parentURL = [parent davURLAsString];
login = [[context activeUser] login];
if ([login isEqualToString: owner])
{
parent = [self privateCalendars: @"Calendar" inContext: context];
parentURL = [parent davURLAsString];
if ([parentURL hasSuffix: @"/"])
parentURL = [parentURL substringToIndex: [parentURL length]-1];
if ([parentURL hasSuffix: @"/"])
parentURL = [parentURL substringToIndex: [parentURL length]-1];
tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D",
[NSString stringWithFormat: @"%@/personal/", parentURL],
nil];
tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D",
[NSString stringWithFormat: @"%@/%@/", parentURL, name],
nil];
response = [NSArray arrayWithObject: tag];
}
else
response = nil;
return [NSArray arrayWithObject: tag];
return response;
}
- (NSArray *) davCalendarScheduleInboxURL
{
NSArray *url;
if ([[context request] isICal4])
url = nil;
else
url = [self _davPersonalCalendarURL];
return url;
return [self _davSpecialCalendarURLWithName: @"inbox"];
}
- (NSArray *) davCalendarScheduleOutboxURL
{
NSArray *url;
if ([[context request] isICal4])
url = nil;
else
url = [self _davPersonalCalendarURL];
return url;
return [self _davSpecialCalendarURLWithName: @"personal"];
}
- (NSArray *) davDropboxHomeURL
- (NSArray *) _calendarProxiedUsersWithWriteAccess: (BOOL) write
{
NSArray *url;
NSMutableArray *proxiedUsers;
SOGoUser *ownerUser;
NSArray *subscriptions;
NSString *ownerLogin, *currentLogin;
int count, max;
if ([[context request] isICal4])
url = nil;
else
url = [self _davPersonalCalendarURL];
return url;
}
- (NSArray *) davNotificationsURL
{
NSArray *url;
if ([[context request] isICal4])
url = nil;
else
url = [self _davPersonalCalendarURL];
return url;
}
- (WOResponse *) _prepareResponseFromContext: (WOContext *) queryContext
{
WOResponse *r;
r = [queryContext response];
[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"];
return r;
}
- (void) _fillMatches: (NSMutableDictionary *) matches
fromElement: (NSObject <DOMElement> *) searchElement
{
NSObject <DOMNodeList> *list;
NSObject <DOMNode> *valueNode;
NSArray *elements;
NSString *property, *match;
list = [searchElement getElementsByTagName: @"prop"];
if ([list length])
{
elements = [self domNode: [list objectAtIndex: 0]
getChildNodesByType: DOM_ELEMENT_NODE];
if ([elements count])
{
valueNode = [elements objectAtIndex: 0];
property = [NSString stringWithFormat: @"{%@}%@",
[valueNode namespaceURI],
[valueNode nodeName]];
}
}
list = [searchElement getElementsByTagName: @"match"];
if ([list length])
{
valueNode = [[list objectAtIndex: 0] firstChild];
match = [valueNode nodeValue];
}
[matches setObject: match forKey: property];
}
- (void) _fillProperties: (NSMutableArray *) properties
fromElement: (NSObject <DOMElement> *) propElement
{
NSEnumerator *elements;
NSObject <DOMElement> *propNode;
NSString *property;
elements = [[self domNode: propElement
getChildNodesByType: DOM_ELEMENT_NODE]
objectEnumerator];
while ((propNode = [elements nextObject]))
{
property = [NSString stringWithFormat: @"{%@}%@",
[propNode namespaceURI],
[propNode nodeName]];
[properties addObject: property];
}
}
- (void) _fillPrincipalMatches: (NSMutableDictionary *) matches
andProperties: (NSMutableArray *) properties
fromElement: (NSObject <DOMElement> *) documentElement
{
NSEnumerator *children;
NSObject <DOMElement> *currentElement;
NSString *tag;
children = [[self domNode: documentElement
getChildNodesByType: DOM_ELEMENT_NODE]
objectEnumerator];
while ((currentElement = [children nextObject]))
{
tag = [currentElement tagName];
if ([tag isEqualToString: @"property-search"])
[self _fillMatches: matches fromElement: currentElement];
else if ([tag isEqualToString: @"prop"])
[self _fillProperties: properties fromElement: currentElement];
else
[self errorWithFormat: @"principal-property-search: unknown tag '%@'",
tag];
}
}
#warning this is a bit ugly, as usual
- (void) _fillCollections: (NSMutableArray *) collections
withCalendarHomeSetMatching: (NSString *) value
{
SOGoUserFolder *collection;
NSRange substringRange;
substringRange = [value rangeOfString: @"/SOGo/dav/"];
value = [value substringFromIndex: NSMaxRange (substringRange)];
substringRange = [value rangeOfString: @"/Calendar"];
value = [value substringToIndex: substringRange.location];
collection = [[SOGoUser userWithLogin: value]
homeFolderInContext: context];
if (collection)
[collections addObject: collection];
}
- (NSMutableArray *) _firstPrincipalCollectionsWhere: (NSString *) key
matches: (NSString *) value
{
NSMutableArray *collections;
collections = [NSMutableArray array];
if ([key
isEqualToString: @"{urn:ietf:params:xml:ns:caldav}calendar-home-set"])
[self _fillCollections: collections withCalendarHomeSetMatching: value];
else
[self errorWithFormat: @"principal-property-search: unhandled key '%@'",
key];
return collections;
}
#warning unused stub
- (BOOL) collectionDavKey: (NSString *) key
matches: (NSString *) value
{
return YES;
}
- (void) _principalCollections: (NSMutableArray **) baseCollections
where: (NSString *) key
matches: (NSString *) value
{
SOGoUserFolder *currentCollection;
unsigned int count, max;
if (!*baseCollections)
*baseCollections = [self _firstPrincipalCollectionsWhere: key
matches: value];
else
{
max = [*baseCollections count];
for (count = max; count > 0; count--)
{
currentCollection = [*baseCollections objectAtIndex: count - 1];
if (![currentCollection collectionDavKey: key matches: value])
[*baseCollections removeObjectAtIndex: count - 1];
}
}
}
- (NSArray *) _principalCollectionsMatching: (NSDictionary *) matches
{
NSMutableArray *collections;
NSEnumerator *allKeys;
NSString *currentKey;
collections = nil;
allKeys = [[matches allKeys] objectEnumerator];
while ((currentKey = [allKeys nextObject]))
[self _principalCollections: &collections
where: currentKey
matches: [matches objectForKey: currentKey]];
return collections;
}
/* <D:principal-property-search xmlns:D="DAV:">
<D:property-search>
<D:prop>
<D:displayname/>
</D:prop>
<D:match>doE</D:match>
</D:property-search>
<D:property-search>
<D:prop xmlns:B="http://www.example.com/ns/">
<B:title/>
</D:prop>
<D:match>Sales</D:match>
</D:property-search>
<D:prop xmlns:B="http://www.example.com/ns/">
<D:displayname/>
<B:department/>
<B:phone/>
<B:office/>
<B:salary/>
</D:prop>
</D:principal-property-search> */
- (void) _appendProperties: (NSArray *) properties
ofCollection: (SOGoUserFolder *) collection
toResponses: (NSMutableArray *) responses
{
unsigned int count, max;
SEL methodSel;
NSString *currentProperty;
id currentValue;
NSMutableArray *response, *props;
NSDictionary *keyTuple;
response = [NSMutableArray array];
[response addObject: davElementWithContent (@"href", XMLNS_WEBDAV,
[collection davURLAsString])];
props = [NSMutableArray array];
max = [properties count];
ownerLogin = [self ownerInContext: nil];
ownerUser = [SOGoUser userWithLogin: ownerLogin];
subscriptions = [[ownerUser userSettings]
calendarProxySubscriptionUsersWithWriteAccess: write];
max = [subscriptions count];
proxiedUsers = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
currentProperty = [properties objectAtIndex: count];
methodSel = SOGoSelectorForPropertyGetter (currentProperty);
if (methodSel && [collection respondsToSelector: methodSel])
{
currentValue = [collection performSelector: methodSel];
#warning evil eVIL EVIl!
if ([currentValue isKindOfClass: [NSArray class]])
{
currentValue = [currentValue objectAtIndex: 0];
currentValue
= davElementWithContent ([currentValue objectAtIndex: 0],
[currentValue objectAtIndex: 1],
[currentValue objectAtIndex: 3]);
}
keyTuple = [currentProperty asWebDAVTuple];
[props addObject: davElementWithContent ([keyTuple objectForKey: @"method"],
[keyTuple objectForKey: @"ns"],
currentValue)];
}
currentLogin = [subscriptions objectAtIndex: count];
[proxiedUsers addObject: currentLogin];
}
[response addObject: davElementWithContent (@"propstat", XMLNS_WEBDAV,
davElementWithContent
(@"prop", XMLNS_WEBDAV,
props))];
[responses addObject: davElementWithContent (@"response", XMLNS_WEBDAV,
response)];
return proxiedUsers;
}
- (void) _appendProperties: (NSArray *) properties
ofCollections: (NSArray *) collections
toResponse: (WOResponse *) response
- (void) _addGroupMembershipToArray: (NSMutableArray *) groups
forWriteAccess: (BOOL) write
{
NSDictionary *mStatus;
NSMutableArray *responses;
unsigned int count, max;
NSArray *proxiedUsers, *tag;
NSString *appName, *groupId, *proxiedUser;
int count, max;
responses = [NSMutableArray new];
max = [collections count];
for (count = 0; count < max; count++)
[self _appendProperties: properties
ofCollection: [collections objectAtIndex: count]
toResponses: responses];
mStatus = davElementWithContent (@"multistatus", XMLNS_WEBDAV, responses);
[response appendContentString: [mStatus asWebDavStringWithNamespaces: nil]];
[responses release];
}
- (WOResponse *) davPrincipalPropertySearch: (WOContext *) queryContext
{
NSObject <DOMDocument> *document;
NSObject <DOMElement> *documentElement;
NSArray *collections;
NSMutableDictionary *matches;
NSMutableArray *properties;
WOResponse *r;
document = [[context request] contentAsDOMDocument];
documentElement = [document documentElement];
matches = [NSMutableDictionary dictionary];
properties = [NSMutableArray array];
[self _fillPrincipalMatches: matches andProperties: properties
fromElement: documentElement];
collections = [self _principalCollectionsMatching: matches];
r = [self _prepareResponseFromContext: queryContext];
[self _appendProperties: properties ofCollections: collections
toResponse: r];
return r;
// @"<D:response><D:href>/SOGo/dav/wsourdeau/</D:href>"
// @"<D:propstat>"
// @"<D:status>HTTP/1.1 200 OK</D:status>"
// @"<D:prop>"
// @"<C:calendar-home-set><D:href>/SOGo/dav/wsourdeau/Calendar/</D:href></C:calendar-home-set>"
// @"<C:calendar-user-address-set><D:href>MAILTO:wsourdeau@inverse.ca</D:href></C:calendar-user-address-set>"
// @"<C:schedule-inbox-URL><D:href>/SOGo/dav/wsourdeau/Calendar/personal/</D:href></C:schedule-inbox-URL>"
// @"<C:schedule-outbox-URL><D:href>/SOGo/dav/wsourdeau/Calendar/personal/</D:href></C:schedule-outbox-URL>"
// @"</D:prop>"
// @"</D:propstat></D:response>"];
// <D:property-search>
// <D:prop>
// <C:calendar-home-set/>
// </D:prop>
// <D:match>/SOGo/dav/wsourdeau/Calendar</D:match>
// </D:property-search>
// <D:prop>
// <C:calendar-home-set/>
// <C:calendar-user-address-set/>
// <C:schedule-inbox-URL/>
// <C:schedule-outbox-URL/>
// </D:prop>
}
- (void) _addFolders: (NSEnumerator *) folders
withGroupTag: (NSString *) groupTag
toArray: (NSMutableArray *) groups
{
SOGoAppointmentFolder *currentFolder;
NSString *folderOwner;
NSArray *tag;
while ((currentFolder = [folders nextObject]))
proxiedUsers = [self _calendarProxiedUsersWithWriteAccess: write];
max = [proxiedUsers count];
if (max)
{
folderOwner = [currentFolder ownerInContext: context];
tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D",
[NSString stringWithFormat: @"/SOGo/dav/%@/%@/",
folderOwner, groupTag],
appName = [[context request] applicationName];
groupId = [NSString stringWithFormat: @"calendar-proxy-%@",
(write ? @"write" : @"read")];
for (count = 0; count < max; count++)
{
proxiedUser = [proxiedUsers objectAtIndex: count];
tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D",
[NSString stringWithFormat: @"/%@/dav/%@/%@/",
appName, proxiedUser, groupId],
nil];
[groups addObject: tag];
[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];
[self _addGroupMembershipToArray: groups
forWriteAccess: YES];
[self _addGroupMembershipToArray: groups
forWriteAccess: NO];
return groups;
}
- (NSMutableArray *) _davCalendarProxyForWrite: (BOOL) write
{
NSMutableArray *proxyFor;
NSArray *proxiedUsers, *tag;
NSString *appName, *proxiedUser;
int count, max;
appName = [[context request] applicationName];
proxiedUsers = [self _calendarProxiedUsersWithWriteAccess: write];
max = [proxiedUsers count];
proxyFor = [NSMutableArray arrayWithCapacity: max];
if (max)
{
for (count = 0; count < max; count++)
{
proxiedUser = [proxiedUsers objectAtIndex: count];
tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D",
[NSString stringWithFormat: @"/%@/dav/%@/",
appName, proxiedUser],
nil];
[proxyFor addObject: tag];
}
}
return proxyFor;
}
- (NSArray *) davCalendarProxyWriteFor
{
return [self _davCalendarProxyForWrite: YES];
}
- (NSArray *) davCalendarProxyReadFor
{
return [self _davCalendarProxyForWrite: NO];
}
@end

View File

@@ -33,7 +33,7 @@
SOGoAppointmentFolder = {
superclass = "SOGoGCSFolder";
defaultRoles = {
/* "FreeBusyLookup" = ( "Owner", "FreeBusy", "AuthorizedSubscriber" ); */
"Read FreeBusy" = ( "Authenticated" );
"Access Contents Information" = ( "Owner", "PublicResponder", "PublicModifier", "PublicViewer", "PublicDAndTViewer", "PrivateResponder", "PrivateModifier", "PrivateViewer", "PrivateDAndTViewer", "ConfidentialResponder", "ConfidentialModifier", "ConfidentialViewer", "ConfidentialDAndTViewer" );
"ViewWholePublicRecords" = ( "Owner", "PublicResponder", "PublicModifier", "PublicViewer" );
"ViewDAndTOfPublicRecords" = ( "Owner", "PublicDAndTViewer" );
@@ -49,6 +49,9 @@
"RespondToConfidentialRecords" = ( "Owner", "ConfidentialModifier", "ConfidentialResponder" );
};
};
SOGoAppointmentInboxFolder = {
superclass = "SOGoAppointmentFolder";
};
// SOGoGroupAppointmentFolder = {
// superclass = "SOGoAppointmentFolder";
// };

View File

@@ -32,6 +32,8 @@
#import <SaxObjC/SaxObjC.h>
#import <SaxObjC/XMLNamespaces.h>
#import <SOGo/WOResponse+SOGo.h>
#import "SOGoContactFolder.h"
#import "SOGoContactGCSEntry.h"
@@ -131,12 +133,7 @@
id <DOMDocument> document;
r = [queryContext response];
[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 prepareDAVResponse];
[r appendContentString: @"<D:multistatus xmlns:D=\"DAV:\""
@" xmlns:C=\"urn:ietf:params:xml:ns:carddav\">"];

View File

@@ -19,6 +19,7 @@
02111-1307, USA.
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSCharacterSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSDictionary.h>

View File

@@ -54,6 +54,7 @@
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/WORequest+SOGo.h>
#import <SOGo/WOResponse+SOGo.h>
#import "EOQualifier+MailDAV.h"
#import "SOGoMailObject.h"
@@ -1372,10 +1373,7 @@ static NSString *defaultUserID = @"anyone";
EOQualifier *searchQualifier;
r = [context response];
[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 prepareDAVResponse];
document = [[context request] contentAsDOMDocument];
documentElement = (DOMElement *) [document documentElement];
@@ -1394,8 +1392,6 @@ static NSString *defaultUserID = @"anyone";
messages = [self _fetchMessageProperties: properties
matchingQualifier: searchQualifier
andSortOrderings: sortOrderings];
[r setStatus: 207];
[r appendContentString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"];
[self _appendProperties: [properties allKeys]
fromMessages: messages
toResponse: r];

View File

@@ -1,4 +1,7 @@
{ /* -*-java-*- */
/* WebDAV */
"{DAV:}expand-property" = davExpandProperty;
/* CalDAV */
"{urn:ietf:params:xml:ns:caldav}calendar-query" = davCalendarQuery;
"{urn:ietf:params:xml:ns:caldav}calendar-multiget" = davCalendarMultiget;
@@ -10,11 +13,13 @@
"{urn:ietf:params:xml:ns:carddav}supported-collation-set"
= davSupportedCollectionSet;
/* DeltaV */
/* ACL */
"{DAV:}acl-principal-prop-set" = davAclPrincipalPropSet;
"{DAV:}principal-match" = davPrincipalMatch;
"{DAV:}principal-property-search" = davPrincipalPropertySearch;
"{DAV:}principal-search-property-set" = davPrincipalSearchPropertySet;
"{DAV:}principal-search-property-set" = davPrincipalSearchPropertySet;
/* WebDAV sync */
"{DAV:}sync-collection" = davSyncCollection;
/* Inverse DAV */

View File

@@ -27,6 +27,13 @@
@class DOMElement;
@interface NGDOMElement (SOGo)
- (NSString *) asPropertyName;
- (NSString *) asPropertyPropertyName;
@end
@interface NGDOMNodeWithChildren (SOGoDOMExtensions)
- (id <DOMNodeList>) childElementsWithTag: (NSString *) tagName;

View File

@@ -24,9 +24,37 @@
#import <Foundation/NSString.h>
#import <DOM/DOMProtocols.h>
#import <DOM/DOMElement.h>
#import <SaxObjC/XMLNamespaces.h>
#import "DOMNode+SOGo.h"
@implementation NGDOMElement (SOGo)
/* returns as "{ns}prop" identifier from an element tag */
- (NSString *) asPropertyName
{
return [NSString stringWithFormat: @"{%@}%@",
[self namespaceURI],
[self tagName]];
}
/* returns as "{ns}prop" identifier from a <property/> tag */
- (NSString *) asPropertyPropertyName
{
NSString *ns, *tag;
ns = [self attribute: @"namespace"];
if (!ns)
ns = XMLNS_WEBDAV;
tag = [self attribute: @"name"];
return [NSString stringWithFormat: @"{%@}%@", ns, tag];
}
@end
@implementation NGDOMNodeWithChildren (SOGo)
- (id <DOMNodeList>) childElementsWithTag: (NSString *) tagName
@@ -93,7 +121,6 @@
NSMutableArray *propertyNames;
id <DOMNodeList> children;
DOMElement *currentElement;
NSString *property;
unsigned int count, max;
propertyNames = [NSMutableArray array];
@@ -104,12 +131,7 @@
{
currentElement = [children objectAtIndex: count];
if ([currentElement nodeType] == DOM_ELEMENT_NODE)
{
property = [NSString stringWithFormat: @"{%@}%@",
[currentElement namespaceURI],
[currentElement tagName]];
[propertyNames addObject: property];
}
[propertyNames addObject: [currentElement asPropertyName]];
}
return propertyNames;

View File

@@ -29,6 +29,9 @@
@interface NSArray (SOGoArrayUtilities)
+ (id) arrayWithObject: (id) member
repeatCount: (int) repeatCount;
- (id *) asPointersOfObjects;
- (NSString *) jsonRepresentation;

View File

@@ -30,6 +30,19 @@
@implementation NSArray (SOGoArrayUtilities)
+ (id) arrayWithObject: (id) member
repeatCount: (int) repeatCount
{
NSMutableArray *newArray;
int count;
newArray = [NSMutableArray arrayWithCapacity: repeatCount];
for (count = 0; count < repeatCount; count++)
[newArray addObject: member];
return newArray;
}
- (id *) asPointersOfObjects
{
id *pointers;

View File

@@ -24,11 +24,21 @@
#define NSOBJECT_DAV_H
#import <Foundation/NSObject.h>
#import <Foundation/NSDictionary.h>
@class NSMutableDictionary;
@class NSString;
@class SoSelectorInvocation;
@class SOGoWebDAVValue;
typedef enum _HTTPStatusCode {
HTTPStatus200 = 0,
HTTPStatus201,
HTTPStatus404,
} HTTPStatusCode;
#define davElement(t,n) \
[NSDictionary dictionaryWithObjectsAndKeys: t, @"method", n, @"ns", nil]
@@ -37,12 +47,26 @@
n, @"ns", \
c, @"content", nil]
SEL SOGoSelectorForPropertyGetter (NSString *property);
SEL SOGoSelectorForPropertySetter (NSString *property);
@interface NSObject (SOGoWebDAVExtensions)
- (NSString *)
asWebDavStringWithNamespaces: (NSMutableDictionary *) namespaces;
- (SOGoWebDAVValue *) asWebDAVValue;
- (SOGoWebDAVValue *) davSupportedReportSet;
- (SEL) davPropertySelectorForKey: (NSString *) key;
- (NSString *) davReportSelectorForKey: (NSString *) key;
- (SoSelectorInvocation *) davReportInvocationForKey: (NSString *) key;
/* response helpers */
- (NSDictionary *) responseForURL: (NSString *) url
withProperties200: (NSArray *) properties200
andProperties404: (NSArray *) properties404;
@end
#endif /* NSOBJECT_DAV_H */

View File

@@ -1,6 +1,6 @@
/* NSObject+DAV.m - this file is part of SOGo
*
* Copyright (C) 2008 Inverse inc.
* Copyright (C) 2008-2010 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
@@ -20,12 +20,110 @@
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSBundle.h>
#import <Foundation/NSFileManager.h>
#import <Foundation/NSValue.h>
#import <NGObjWeb/WOApplication.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/SoSelectorInvocation.h>
#import <NGObjWeb/SoObject+SoDAV.h>
#import <SaxObjC/XMLNamespaces.h>
#import <NGExtensions/NSObject+Logs.h>
#import "NSArray+DAV.h"
#import "NSString+DAV.h"
#import "SOGoUser.h"
#import "SOGoUserFolder.h"
#import "SOGoWebDAVValue.h"
#import "SOGoObject.h"
#import "NSObject+DAV.h"
static NSMutableDictionary *setterMap = nil;
static NSMutableDictionary *getterMap = nil;
static NSDictionary *reportMap = 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];
}
else
propSel = NULL;
}
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];
}
else
propSel = NULL;
}
return propSel;
}
@implementation NSObject (SOGoWebDAVExtensions)
- (void) loadReportMAP
{
NSBundle *bundle;
NSString *filename;
bundle = [NSBundle bundleForClass: [SOGoObject class]];
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!"];
}
- (NSString *)
asWebDavStringWithNamespaces: (NSMutableDictionary *) namespaces
{
@@ -41,4 +139,130 @@
attributes: nil];
}
- (SEL) davPropertySelectorForKey: (NSString *) key
{
static NSMutableDictionary *attrSelectorMap = nil;
NSDictionary *attrMap;
NSString *methodName;
NSValue *methodValue;
SEL propertySel;
methodValue = [attrSelectorMap objectForKey: key];
if (!methodValue)
{
if (!attrSelectorMap)
attrSelectorMap = [NSMutableDictionary new];
attrMap = [[self class] defaultWebDAVAttributeMap];
methodName = [attrMap objectForKey: key];
if (methodName)
propertySel = NSSelectorFromString (methodName);
else
propertySel = NULL;
methodValue = [NSValue valueWithPointer: propertySel];
[attrSelectorMap setObject: methodValue forKey: key];
}
return [methodValue pointerValue];
}
- (NSString *) davReportSelectorForKey: (NSString *) key
{
NSString *methodName, *objcMethod, *resultName;
SEL reportSel;
resultName = nil;
if (!reportMap)
[self loadReportMAP];
methodName = [reportMap objectForKey: key];
if (methodName)
{
objcMethod = [NSString stringWithFormat: @"%@:", methodName];
reportSel = NSSelectorFromString (objcMethod);
if ([self respondsToSelector: reportSel])
resultName = objcMethod;
}
return resultName;
}
- (SoSelectorInvocation *) davReportInvocationForKey: (NSString *) key
{
NSString *objCMethod;
SoSelectorInvocation *invocation;
objCMethod = [self davReportSelectorForKey: key];
if (objCMethod)
{
invocation = [[SoSelectorInvocation alloc]
initWithSelectorNamed: objCMethod
addContextParameter: YES];
[invocation autorelease];
}
else
invocation = nil;
return invocation;
}
- (SOGoWebDAVValue *) davSupportedReportSet
{
NSDictionary *currentValue;
NSEnumerator *reportKeys;
NSMutableArray *reportSet;
NSString *currentKey;
reportSet = [NSMutableArray array];
if (!reportMap)
[self loadReportMAP];
reportKeys = [[reportMap allKeys] objectEnumerator];
while ((currentKey = [reportKeys nextObject]))
if ([self davReportSelectorForKey: currentKey])
{
currentValue = davElementWithContent(@"report",
@"DAV:",
[currentKey asDavInvocation]);
[reportSet addObject: davElementWithContent(@"supported-report",
@"DAV:", currentValue)];
}
return [davElementWithContent (@"supported-report-set", @"DAV:", reportSet)
asWebDAVValue];
}
- (NSDictionary *) responseForURL: (NSString *) url
withProperties200: (NSArray *) properties200
andProperties404: (NSArray *) properties404
{
static NSString *statusStrings[] = { @"HTTP/1.1 200 OK",
@"HTTP/1.1 201 Created",
@"HTTP/1.1 404 Not Found" };
NSString *status;
NSDictionary *responseElement;
NSMutableArray *elements;
elements = [NSMutableArray arrayWithCapacity: 3];
[elements addObject: davElementWithContent (@"href", XMLNS_WEBDAV,
url)];
if ([properties200 count])
{
status = statusStrings[HTTPStatus200];
[elements addObject: [properties200 asDAVPropstatWithStatus: status]];
}
if ([properties404 count])
{
status = statusStrings[HTTPStatus404];
[elements addObject: [properties404 asDAVPropstatWithStatus: status]];
}
responseElement = davElementWithContent (@"response", XMLNS_WEBDAV,
elements);
return responseElement;
}
@end

View File

@@ -25,12 +25,17 @@
#import <Foundation/NSObject.h>
#import <DOM/DOMProtocols.h>
@class NSString;
@interface NSObject (SOGoObjectUtilities)
- (NSString *) jsonRepresentation;
- (NSArray *) domNode: (id <DOMNode>) node
getChildNodesByType: (DOMNodeType) type;
@end
#endif /* NSOBJECT+UTILITIES_H */

View File

@@ -20,6 +20,7 @@
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
#import "NSObject+Utilities.h"
@@ -33,4 +34,23 @@
return nil;
}
- (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

View File

@@ -34,6 +34,13 @@
- (NSMutableDictionary *) asWebDAVTuple;
- (NSMutableDictionary *) asWebDAVTupleWithContent: (id) content;
- (NSString *) davMethodToObjC;
- (NSString *) davSetterName;
- (NSDictionary *) asDavInvocation;
- (NSString *) removeOutsideTags;
@end
#endif /* NSSTRING_DAV_H */

View File

@@ -24,6 +24,8 @@
#import <NGExtensions/NSString+misc.h>
#import "NSArray+Utilities.h"
#import "NSString+DAV.h"
@implementation NSString (SOGoWebDAVExtensions)
@@ -34,6 +36,16 @@
return [self stringByEscapingXMLString];
}
- (NSString *) asDAVPropertyDescription
{
NSArray *components;
components = [[self componentsSeparatedByString: @"-"]
resultsOfSelector: @selector (capitalizedString)];
return [components componentsJoinedByString: @" "];
}
#warning we should use the same nomenclature as the webdav values...
- (NSMutableDictionary *) asWebDAVTuple
{
@@ -49,6 +61,60 @@
nodeName, @"method", nil];
}
- (NSString *) davMethodToObjC
{
NSMutableString *newName;
NSArray *components;
newName = [NSMutableString stringWithString: @"dav"];
components = [[self componentsSeparatedByString: @"-"]
resultsOfSelector: @selector (capitalizedString)];
[newName appendString: [components componentsJoinedByString: @""]];
return newName;
}
- (NSString *) davSetterName
{
unichar firstLetter;
NSString *firstString;
firstLetter = [self characterAtIndex: 0];
firstString = [[NSString stringWithCharacters: &firstLetter length: 1]
uppercaseString];
return [NSString stringWithFormat: @"set%@%@:",
firstString, [self substringFromIndex: 1]];
}
- (NSDictionary *) asDavInvocation
{
NSMutableDictionary *davInvocation;
NSRange nsEnclosing, methodEnclosing;
unsigned int length;
davInvocation = nil;
if ([self hasPrefix: @"{"])
{
nsEnclosing = [self rangeOfString: @"}"];
length = [self length];
if (nsEnclosing.length > 0 && nsEnclosing.location < (length - 1))
{
methodEnclosing = NSMakeRange (nsEnclosing.location + 1,
length - nsEnclosing.location - 1);
nsEnclosing.length = nsEnclosing.location - 1;
nsEnclosing.location = 1;
davInvocation = [NSMutableDictionary dictionaryWithCapacity: 2];
[davInvocation setObject: [self substringWithRange: nsEnclosing]
forKey: @"ns"];
[davInvocation setObject: [self substringWithRange: methodEnclosing]
forKey: @"method"];
}
}
return davInvocation;
}
- (NSMutableDictionary *) asWebDAVTupleWithContent: (id) content
{
NSMutableDictionary *tuple;
@@ -59,4 +125,28 @@
return tuple;
}
- (NSString *) removeOutsideTags
{
NSString *newString;
NSRange range;
newString = nil;
/* we extract the text between >XXX</, but in a bad way */
range = [self rangeOfString: @">"];
if (range.location != NSNotFound)
{
newString = [self substringFromIndex: range.location + 1];
range = [newString rangeOfString: @"<" options: NSBackwardsSearch];
if (range.location != NSNotFound)
newString = [newString substringToIndex: range.location];
else
newString = nil;
}
else
newString = nil;
return newString;
}
@end

View File

@@ -36,10 +36,6 @@
- (NSString *) urlWithoutParameters;
- (NSString *) davMethodToObjC;
- (NSString *) davSetterName;
- (NSDictionary *) asDavInvocation;
- (NSString *) stringByDetectingURLs;
- (NSString *) doubleQuotedString;

View File

@@ -102,60 +102,6 @@ static int cssEscapingCount;
return newUrl;
}
- (NSString *) davMethodToObjC
{
NSMutableString *newName;
NSEnumerator *components;
NSString *component;
newName = [NSMutableString stringWithString: @"dav"];
components = [[self componentsSeparatedByString: @"-"] objectEnumerator];
while ((component = [components nextObject]))
[newName appendString: [component capitalizedString]];
return newName;
}
- (NSString *) davSetterName
{
unichar firstLetter;
NSString *firstString;
firstLetter = [self characterAtIndex: 0];
firstString = [[NSString stringWithCharacters: &firstLetter length: 1]
uppercaseString];
return [NSString stringWithFormat: @"set%@%@:",
firstString, [self substringFromIndex: 1]];
}
- (NSDictionary *) asDavInvocation
{
NSMutableDictionary *davInvocation;
NSRange nsEnclosing, methodEnclosing;
unsigned int length;
davInvocation = nil;
if ([self hasPrefix: @"{"])
{
nsEnclosing = [self rangeOfString: @"}"];
length = [self length];
if (nsEnclosing.length > 0 && nsEnclosing.location < (length - 1))
{
methodEnclosing = NSMakeRange (nsEnclosing.location + 1,
length - nsEnclosing.location - 1);
nsEnclosing.length = nsEnclosing.location - 1;
nsEnclosing.location = 1;
davInvocation = [NSMutableDictionary dictionaryWithCapacity: 2];
[davInvocation setObject: [self substringWithRange: nsEnclosing]
forKey: @"ns"];
[davInvocation setObject: [self substringWithRange: methodEnclosing]
forKey: @"method"];
}
}
return davInvocation;
}
- (NSRange) _rangeOfURLInRange: (NSRange) refRange
{
int start, length;

View File

@@ -25,14 +25,25 @@
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSString.h>
#import <Foundation/NSURL.h>
#import <Foundation/NSValue.h>
#import <NGObjWeb/WOContext.h>
#import <NGObjWeb/SoWebDAVValue.h>
#import <NGExtensions/NSObject+Logs.h>
#import <DOM/DOMElement.h>
#import <SaxObjC/XMLNamespaces.h>
#import "DOMNode+SOGo.h"
#import "NSArray+Utilities.h"
#import "NSObject+DAV.h"
#import "NSString+DAV.h"
#import "NSString+Utilities.h"
#import "SOGoPermissions.h"
#import "SOGoWebDAVAclManager.h"
#import "WOResponse+SOGo.h"
#import "SOGoFolder.h"
@@ -42,6 +53,14 @@
@end
@interface SOGoFolder (private)
- (NSArray *) _interpretWebDAVArrayValue: (id) value;
- (NSDictionary *) _expandPropertyResponse: (NGDOMElement *) property
forHREF: (NSString *) href;
@end
@implementation SOGoFolder
+ (SOGoWebDAVAclManager *) webdavAclManager
@@ -51,14 +70,14 @@
if (!webdavAclManager)
{
webdavAclManager = [SOGoWebDAVAclManager new];
[webdavAclManager registerDAVPermission: davElement (@"read", @"DAV:")
[webdavAclManager registerDAVPermission: davElement (@"read", XMLNS_WEBDAV)
abstract: YES
withEquivalent: SoPerm_WebDAVAccess
asChildOf: davElement (@"all", @"DAV:")];
[webdavAclManager registerDAVPermission: davElement (@"read-current-user-privilege-set", @"DAV:")
asChildOf: davElement (@"all", XMLNS_WEBDAV)];
[webdavAclManager registerDAVPermission: davElement (@"read-current-user-privilege-set", XMLNS_WEBDAV)
abstract: YES
withEquivalent: nil
asChildOf: davElement (@"read", @"DAV:")];
asChildOf: davElement (@"read", XMLNS_WEBDAV)];
}
return webdavAclManager;
@@ -150,6 +169,12 @@
return YES;
}
- (NSString *) davURLAsString
{
return [[container davURLAsString]
stringByAppendingFormat: @"%@/", nameInContainer];
}
- (NSString *) httpURLForAdvisoryToUser: (NSString *) uid
{
return [[self soURL] absoluteString];
@@ -250,6 +275,218 @@
return YES;
}
- (NSArray *) _extractHREFSFromPropertyValues: (NSArray *) values
{
NSMutableArray *hrefs;
NSDictionary *value;
int count, max;
max = [values count];
hrefs = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
value = [values objectAtIndex: count];
if ([value isKindOfClass: [NSDictionary class]])
{
if ([[value objectForKey: @"method"] isEqualToString: @"href"])
[hrefs addObject: [value objectForKey: @"content"]];
else
[self errorWithFormat: @"value is not an href"];
}
else if ([value isKindOfClass: [NSString class]])
{
/* we extract the text between <href>XXX</href>, but in a bad way */
[hrefs addObject: [(NSString *) value removeOutsideTags]];
}
else
[self errorWithFormat: @"value class is '%@' instead of NSDictionary",
NSStringFromClass ([value class])];
}
return hrefs;
}
- (NSArray *) _interpretSoWebDAVValue: (SoWebDAVValue *) value
{
NSString *valueString;
valueString = [value stringForTag: @"" rawName: @""
inContext: context prefixes: nil];
return [NSArray arrayWithObject: [valueString removeOutsideTags]];
}
- (NSArray *) _interpretWebDAVValue: (id) value
{
NSArray *result;
if ([value isKindOfClass: [NSString class]])
result = [NSArray arrayWithObject: value];
else if ([value isKindOfClass: [SoWebDAVValue class]])
result = [self _interpretSoWebDAVValue: value];
else if ([value isKindOfClass: [NSArray class]])
result = [self _interpretWebDAVArrayValue: value];
else
result = nil;
return result;
}
- (NSArray *) _interpretWebDAVArrayValue: (id) value
{
int count, max;
NSMutableArray *results;
id subValue, subResults;
max = [value count];
results = [NSMutableArray arrayWithCapacity: max];
if (max > 0)
{
subValue = [value objectAtIndex: 0];
if ([subValue isKindOfClass: [NSString class]])
[results addObject:
davElementWithContent (subValue,
[value objectAtIndex: 1],
[value objectAtIndex: 3])];
else
{
for (count = 0; count < max; count++)
{
subResults
= [self _interpretWebDAVValue: [value objectAtIndex: count]];
[results addObjectsFromArray: subResults];
}
}
}
return results;
}
- (NSArray *) _expandedPropertyValue: (NGDOMElement *) property
forObject: (SOGoObject *) currentObject
{
NSString *propertyTag;
SEL propertySel;
id value;
propertyTag = [property asPropertyPropertyName];
propertySel = [self davPropertySelectorForKey: propertyTag];
if (propertySel)
value = [currentObject performSelector: propertySel];
else
value = nil;
return [self _interpretWebDAVValue: value];
}
- (NSArray *) _expandPropertyValue: (NGDOMElement *) property
forObject: (SOGoObject *) currentObject
{
NSArray *values, *hrefs;
NSString *href;
NSMutableArray *expandedValues;
int count, max;
BOOL needsExpansion;
needsExpansion = ([[property childElementsWithTag: @"property"] length] > 0);
values = [self _expandedPropertyValue: property
forObject: currentObject];
max = [values count];
expandedValues = [NSMutableArray arrayWithCapacity: max];
if (max)
{
if (needsExpansion)
{
hrefs = [self _extractHREFSFromPropertyValues: values];
max = [hrefs count];
for (count = 0; count < max; count++)
{
href = [hrefs objectAtIndex: count];
[expandedValues addObject: [self _expandPropertyResponse: property
forHREF: href]];
}
}
else
[expandedValues setArray: values];
}
return expandedValues;
}
- (NSDictionary *) _expandPropertyResponse: (NGDOMElement *) property
forObject: (SOGoObject *) currentObject
{
id <DOMNodeList> properties;
NSArray *childValue;
NGDOMElement *childProperty;
NSDictionary *response;
NSMutableArray *properties200, *properties404;
int count, max;
NSString *tagName, *tagNS;
properties = [property childElementsWithTag: @"property"];
max = [properties length];
properties200 = [NSMutableArray arrayWithCapacity: max];
properties404 = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
childProperty = [properties objectAtIndex: count];
childValue = [self _expandPropertyValue: childProperty
forObject: currentObject];
tagNS = [childProperty attribute: @"namespace"];
if (!tagNS)
tagNS = XMLNS_WEBDAV;
tagName = [childProperty attribute: @"name"];
if ([childValue count])
[properties200 addObject: davElementWithContent (tagName,
tagNS,
childValue)];
else
[properties404 addObject: davElement (tagName, tagNS)];
}
response = [self responseForURL: [currentObject davURLAsString]
withProperties200: properties200
andProperties404: properties404];
return response;
}
- (NSDictionary *) _expandPropertyResponse: (NGDOMElement *) property
forHREF: (NSString *) href
{
SOGoObject *lookupObject;
NSDictionary *response;
lookupObject = [self lookupObjectAtDAVUrl: href];
if (lookupObject)
response = [self _expandPropertyResponse: property
forObject: lookupObject];
else
response = nil;
return response;
}
- (WOResponse *) davExpandProperty: (WOContext *) localContext
{
WOResponse *r;
id <DOMDocument> document;
NSDictionary *response, *multistatus;
NGDOMElement *documentElement;
r = [localContext response];
[r prepareDAVResponse];
document = [[context request] contentAsDOMDocument];
documentElement = (NGDOMElement *) [document documentElement];
response = [self _expandPropertyResponse: documentElement forObject: self];
multistatus = davElementWithContent (@"multistatus", XMLNS_WEBDAV,
response);
[r appendContentString: [multistatus asWebDavStringWithNamespaces: nil]];
return r;
}
/* web dav acl helper */
- (void) _fillArrayWithPrincipalsOwnedBySelf: (NSMutableArray *) hrefs
{

View File

@@ -89,14 +89,14 @@ static NSArray *childRecordFields = nil;
if (!aclManager)
{
aclManager = [SOGoWebDAVAclManager new];
[aclManager registerDAVPermission: davElement (@"read", @"DAV:")
/* [aclManager registerDAVPermission: davElement (@"read", @"DAV:")
abstract: YES
withEquivalent: SoPerm_WebDAVAccess
asChildOf: davElement (@"all", @"DAV:")];
[aclManager registerDAVPermission: davElement (@"read-current-user-privilege-set", @"DAV:")
abstract: YES
withEquivalent: SoPerm_WebDAVAccess
asChildOf: davElement (@"read", @"DAV:")];
asChildOf: davElement (@"read", @"DAV:")]; */
[aclManager registerDAVPermission: davElement (@"write", @"DAV:")
abstract: YES
withEquivalent: nil
@@ -156,6 +156,7 @@ static NSArray *childRecordFields = nil;
NSArray *elements, *pathElements;
NSString *path, *objectPath, *login, *currentUser, *ocsName, *folderName;
WOContext *context;
BOOL isSubscription;
elements = [reference componentsSeparatedByString: @":"];
login = [elements objectAtIndex: 0];
@@ -175,7 +176,8 @@ static NSArray *childRecordFields = nil;
newFolder = [self objectWithName: folderName inContainer: aContainer];
[newFolder setOCSPath: path];
[newFolder setOwner: login];
[newFolder setIsSubscription: ![login isEqualToString: currentUser]];
isSubscription = ![login isEqualToString: [aContainer ownerInContext: context]];
[newFolder setIsSubscription: isSubscription];
if (![newFolder displayName])
newFolder = nil;
@@ -1111,10 +1113,7 @@ static NSArray *childRecordFields = nil;
NSArray *records;
r = [context response];
[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 prepareDAVResponse];
document = [[context request] contentAsDOMDocument];
documentElement = (DOMElement *) [document documentElement];
@@ -1127,15 +1126,10 @@ static NSArray *childRecordFields = nil;
records = [self _fetchSyncTokenFields: properties
matchingSyncToken: syncToken];
if (![syncToken length] || [records count])
{
[r setStatus: 207];
[r appendContentString: @"<?xml version=\"1.0\""
@" encoding=\"utf-8\"?>\r\n"];
[self _appendComponentProperties: [properties allKeys]
fromRecords: records
matchingSyncToken: [syncToken intValue]
toResponse: r];
}
[self _appendComponentProperties: [properties allKeys]
fromRecords: records
matchingSyncToken: [syncToken intValue]
toResponse: r];
else
[r appendDAVError: davElement (@"valid-sync-token", XMLNS_WEBDAV)];
@@ -1340,8 +1334,8 @@ static NSArray *childRecordFields = nil;
[aclsForObject removeObjectsForKeys: usersAndGroups];
uids = [usersAndGroups componentsJoinedByString: @"') OR (c_uid = '"];
qs = [NSString
stringWithFormat: @"(c_object = '/%@') AND ((c_uid = '%@'))",
objectPath, uids];
stringWithFormat: @"(c_object = '/%@') AND ((c_uid = '%@'))",
objectPath, uids];
qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
[[self ocsFolder] deleteAclMatchingQualifier: qualifier];
}
@@ -1363,10 +1357,10 @@ static NSArray *childRecordFields = nil;
while ((currentRole = [userRoles nextObject]))
{
SQL = [NSString stringWithFormat: @"INSERT INTO %@"
@" (c_object, c_uid, c_role)"
@" VALUES ('/%@', '%@', '%@')",
[folder aclTableName],
objectPath, uid, currentRole];
@" (c_object, c_uid, c_role)"
@" VALUES ('/%@', '%@', '%@')",
[folder aclTableName],
objectPath, uid, currentRole];
[channel evaluateExpressionX: SQL];
}
@@ -1437,9 +1431,9 @@ static NSArray *childRecordFields = nil;
- (void) setRoles: (NSArray *) roles
forUser: (NSString *) uid
{
return [self setRoles: roles
forUser: uid
forObjectAtPath: [self pathArrayToFolder]];
return [self setRoles: roles
forUser: uid
forObjectAtPath: [self pathArrayToFolder]];
}
- (void) removeAclsForUsers: (NSArray *) users

View File

@@ -24,8 +24,6 @@
#import <Foundation/NSObject.h>
#import <DOM/DOMProtocols.h>
#if LIB_FOUNDATION_LIBRARY
#error SOGo will not work properly with libFoundation.
#error Please use gnustep-base instead.
@@ -62,9 +60,6 @@
#define $(class) NSClassFromString(class)
SEL SOGoSelectorForPropertyGetter (NSString *property);
SEL SOGoSelectorForPropertySetter (NSString *property);
@interface SOGoObject : NSObject
{
WOContext *context;
@@ -132,6 +127,8 @@ SEL SOGoSelectorForPropertySetter (NSString *property);
- (NSArray *) aclsForUser: (NSString *) uid;
- (void) setRoles: (NSArray *) roles
forUser: (NSString *) uid;
- (void) setRoles: (NSArray *) roles
forUsers: (NSArray *) users;
- (void) removeAclsForUsers: (NSArray *) users;
- (NSString *) defaultUserID;
@@ -141,6 +138,8 @@ SEL SOGoSelectorForPropertySetter (NSString *property);
- (NSString *) httpURLForAdvisoryToUser: (NSString *) uid;
- (NSString *) resourceURLForAdvisoryToUser: (NSString *) uid;
- (SOGoObject *) lookupObjectAtDAVUrl: (NSString *) davURL;
- (NSArray *) davComplianceClassesInContext: (WOContext *) localContext;
- (id) davPOSTRequest: (WORequest *) request
@@ -166,11 +165,4 @@ SEL SOGoSelectorForPropertySetter (NSString *property);
@end
@interface SOGoObject (SOGoDomHelpers)
- (NSArray *) domNode: (id <DOMNode>) node
getChildNodesByType: (DOMNodeType) type;
@end
#endif /* __SoObjects_SOGoObject_H__ */

View File

@@ -33,7 +33,6 @@
#import <NGObjWeb/SoClass.h>
#import <NGObjWeb/SoObject+SoDAV.h>
#import <NGObjWeb/SoSelectorInvocation.h>
#import <NGObjWeb/WEClientCapabilities.h>
#import <NGObjWeb/WOApplication.h>
#import <NGObjWeb/WOContext.h>
@@ -48,6 +47,8 @@
#import <DOM/DOMNode.h>
#import <DOM/DOMProtocols.h>
#import <NGCards/NSDictionary+NGCards.h>
#import <SaxObjC/XMLNamespaces.h>
#import <UI/SOGoUI/SOGoACLAdvisory.h>
#import "NSArray+Utilities.h"
@@ -55,6 +56,7 @@
#import "NSDictionary+Utilities.h"
#import "NSObject+DAV.h"
#import "NSObject+Utilities.h"
#import "NSString+DAV.h"
#import "NSString+Utilities.h"
#import "SOGoCache.h"
#import "SOGoDomainDefaults.h"
@@ -65,73 +67,11 @@
#import "SOGoUserFolder.h"
#import "SOGoWebDAVAclManager.h"
#import "SOGoWebDAVValue.h"
#import "WORequest+SOGo.h"
#import "WOResponse+SOGo.h"
#import "SOGoObject.h"
static NSDictionary *reportMap = nil;
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];
}
else
propSel = NULL;
}
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];
}
else
propSel = NULL;
}
return propSel;
}
@implementation SOGoObject
+ (SOGoWebDAVAclManager *) webdavAclManager
@@ -152,15 +92,15 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
if (!permissions)
{
permissions = [NSDictionary dictionaryWithObjectsAndKeys:
davElement (@"read", @"DAV:"),
davElement (@"read", XMLNS_WEBDAV),
SoPerm_AccessContentsInformation,
davElement (@"bind", @"DAV:"),
davElement (@"bind", XMLNS_WEBDAV),
SoPerm_AddDocumentsImagesAndFiles,
davElement (@"unbind", @"DAV:"),
davElement (@"unbind", XMLNS_WEBDAV),
SoPerm_DeleteObjects,
davElement (@"write-acl", @"DAV:"),
davElement (@"write-acl", XMLNS_WEBDAV),
SoPerm_ChangePermissions,
davElement (@"write-content", @"DAV:"),
davElement (@"write-content", XMLNS_WEBDAV),
SoPerm_ChangeImagesAndFiles, NULL];
[permissions retain];
}
@@ -168,36 +108,6 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
return permissions;
} */
+ (void) initialize
{
NSString *filename;
NSBundle *bundle;
if (!reportMap)
{
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!"];
}
// SoClass security declarations
// require View permission to access the root (bound to authenticated ...)
// [[self soClassSecurityInfo] declareObjectProtected: SoPerm_View];
// to allow public access to all contained objects (subkeys)
// [[self soClassSecurityInfo] setDefaultAccess: @"allow"];
// /* require Authenticated role for View and WebDAV */
// [[self soClassSecurityInfo] declareRole: SoRole_Owner
// asDefaultForPermission: SoPerm_View];
// [[self soClassSecurityInfo] declareRole: SoRole_Owner
// asDefaultForPermission: SoPerm_WebDAVAccess];
}
+ (NSString *) globallyUniqueObjectId
{
/*
@@ -349,32 +259,13 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
return ma;
}
- (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;
}
- (id) lookupName: (NSString *) lookupName
inContext: (id) localContext
acquire: (BOOL) acquire
{
id obj;
SOGoCache *cache;
NSString *objcMethod, *httpMethod;
NSString *httpMethod;
cache = [SOGoCache sharedCache];
obj = [cache objectNamed: lookupName inContainer: self];
@@ -382,16 +273,7 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
{
httpMethod = [[localContext request] method];
if ([httpMethod isEqualToString: @"REPORT"])
{
objcMethod = [self _reportSelector: lookupName];
if (objcMethod)
{
obj = [[SoSelectorInvocation alloc]
initWithSelectorNamed: objcMethod
addContextParameter: YES];
[obj autorelease];
}
}
obj = [self davReportInvocationForKey: lookupName];
else
{
obj = [[self soClass] lookupKey: lookupName inContext: localContext];
@@ -458,9 +340,9 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
usersUrl = [NSString stringWithFormat: @"%@%@/",
[[WOApplication application] davURLAsString], owner];
ownerHREF = davElementWithContent (@"href", @"DAV:", usersUrl);
ownerHREF = davElementWithContent (@"href", XMLNS_WEBDAV, usersUrl);
return [davElementWithContent (@"owner", @"DAV:", ownerHREF)
return [davElementWithContent (@"owner", XMLNS_WEBDAV, ownerHREF)
asWebDAVValue];
}
@@ -469,11 +351,11 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
NSArray *restrictions;
restrictions = [NSArray arrayWithObjects:
davElement (@"grant-only", @"DAV:"),
davElement (@"no-invert", @"DAV:"),
davElement (@"grant-only", XMLNS_WEBDAV),
davElement (@"no-invert", XMLNS_WEBDAV),
nil];
return [davElementWithContent (@"acl-restrictions", @"DAV:", restrictions)
return [davElementWithContent (@"acl-restrictions", XMLNS_WEBDAV, restrictions)
asWebDAVValue];
}
@@ -481,15 +363,22 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
{
NSString *usersUrl;
NSDictionary *collectionHREF;
NSString *classes;
if ([[context request] isICal4])
{
classes = [[self davComplianceClassesInContext: context]
componentsJoinedByString: @", "];
[[context response] setHeader: classes forKey: @"DAV"];
}
/* 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] davURLAsString], owner];
collectionHREF = davElementWithContent (@"href", @"DAV:", usersUrl);
usersUrl = [[WOApplication application] davURLAsString];
collectionHREF = davElementWithContent (@"href", XMLNS_WEBDAV, usersUrl);
return [davElementWithContent (@"principal-collection-set",
@"DAV:",
XMLNS_WEBDAV,
[NSArray arrayWithObject: collectionHREF])
asWebDAVValue];
}
@@ -505,7 +394,7 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
privileges = [[webdavAclManager davPermissionsForRoles: roles
onObject: self] objectEnumerator];
while ((privilege = [privileges nextObject]))
[davPrivileges addObject: davElementWithContent (@"privilege", @"DAV:",
[davPrivileges addObject: davElementWithContent (@"privilege", XMLNS_WEBDAV,
privilege)];
return davPrivileges;
@@ -518,7 +407,7 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
userRoles = [[context activeUser] rolesForObject: self inContext: context];
return [davElementWithContent (@"current-user-privilege-set",
@"DAV:",
XMLNS_WEBDAV,
[self _davPrivilegesFromRoles: userRoles])
asWebDAVValue];
}
@@ -526,7 +415,7 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
- (SOGoWebDAVValue *) davSupportedPrivilegeSet
{
return [davElementWithContent (@"supported-privilege-set",
@"DAV:",
XMLNS_WEBDAV,
[webdavAclManager treeAsWebDAVValue])
asWebDAVValue];
}
@@ -549,67 +438,85 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
principalURL = [NSString stringWithFormat: @"%@%@/",
[[WOApplication application] davURLAsString],
currentUID];
userHREF = davElementWithContent (@"href", @"DAV:", principalURL);
[currentAce addObject: davElementWithContent (@"principal", @"DAV:",
userHREF = davElementWithContent (@"href", XMLNS_WEBDAV, principalURL);
[currentAce addObject: davElementWithContent (@"principal", XMLNS_WEBDAV,
userHREF)];
currentGrant
= davElementWithContent (@"grant", @"DAV:",
= davElementWithContent (@"grant", XMLNS_WEBDAV,
[self _davPrivilegesFromRoles: roles]);
[currentAce addObject: currentGrant];
[aces addObject: davElementWithContent (@"ace", @"DAV:", currentAce)];
[aces addObject: davElementWithContent (@"ace", XMLNS_WEBDAV, currentAce)];
}
}
- (void) _fillAcesWithRolesForPseudoPrincipals: (NSMutableArray *) aces
{
NSArray *roles, *currentAce;
NSDictionary *principal, *currentGrant;
NSDictionary *principal, *currentGrant, *principalHREF, *inherited;
NSString *principalURL;
SOGoUser *user;
// DAV:self, DAV:property(owner), DAV:authenticated
user = [context activeUser];
principalURL = [NSString stringWithFormat: @"%@%@/",
[[WOApplication application] davURLAsString],
[self ownerInContext: nil]];
principalHREF = davElementWithContent (@"href", XMLNS_WEBDAV, principalURL);
inherited = davElementWithContent (@"inherited", XMLNS_WEBDAV,
principalHREF),
roles = [user rolesForObject: self inContext: context];
if ([roles count])
{
principal = davElement (@"self", @"DAV:");
principal = davElement (@"self", XMLNS_WEBDAV);
currentGrant
= davElementWithContent (@"grant", @"DAV:",
= davElementWithContent (@"grant", XMLNS_WEBDAV,
[self _davPrivilegesFromRoles: roles]);
currentAce = [NSArray arrayWithObjects:
davElementWithContent (@"principal", @"DAV:",
principal),
currentGrant, nil];
[aces addObject: davElementWithContent (@"ace", @"DAV:", currentAce)];
currentAce = [NSArray
arrayWithObjects: davElementWithContent (@"principal",
XMLNS_WEBDAV,
principal),
currentGrant,
inherited,
nil];
[aces addObject: davElementWithContent (@"ace", XMLNS_WEBDAV, 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:"));
principal = davElementWithContent (@"property", XMLNS_WEBDAV,
davElement (@"owner", XMLNS_WEBDAV));
currentGrant
= davElementWithContent (@"grant", @"DAV:",
= davElementWithContent (@"grant", XMLNS_WEBDAV,
[self _davPrivilegesFromRoles: roles]);
currentAce = [NSArray arrayWithObjects:
davElementWithContent (@"principal", @"DAV:",
principal),
currentGrant, nil];
[aces addObject: davElementWithContent (@"ace", @"DAV:", currentAce)];
currentAce = [NSArray
arrayWithObjects: davElementWithContent (@"principal",
XMLNS_WEBDAV,
principal),
currentGrant,
inherited,
nil];
[aces addObject: davElementWithContent (@"ace", XMLNS_WEBDAV, currentAce)];
}
roles = [self aclsForUser: [self defaultUserID]];
if ([roles count])
{
principal = davElement (@"authenticated", @"DAV:");
principal = davElement (@"authenticated", XMLNS_WEBDAV);
currentGrant
= davElementWithContent (@"grant", @"DAV:",
= davElementWithContent (@"grant", XMLNS_WEBDAV,
[self _davPrivilegesFromRoles: roles]);
currentAce = [NSArray arrayWithObjects:
davElementWithContent (@"principal", @"DAV:",
principal),
currentGrant, nil];
[aces addObject: davElementWithContent (@"ace", @"DAV:", currentAce)];
currentAce = [NSArray
arrayWithObjects: davElementWithContent (@"principal",
XMLNS_WEBDAV,
principal),
currentGrant,
inherited,
nil];
[aces addObject: davElementWithContent (@"ace", XMLNS_WEBDAV, currentAce)];
}
}
@@ -626,133 +533,10 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
while ((currentUID = [uids nextObject]))
[self _fillAces: aces withRolesForUID: currentUID];
return [davElementWithContent (@"acl", @"DAV:", aces)
return [davElementWithContent (@"acl", XMLNS_WEBDAV, aces)
asWebDAVValue];
}
#warning all REPORT method should be standardized...
- (NSDictionary *) _formalizePrincipalMatchResponse: (NSArray *) hrefs
{
NSDictionary *multiStatus;
NSEnumerator *hrefList;
NSString *currentHref;
NSMutableArray *responses;
NSArray *responseElements;
responses = [NSMutableArray array];
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] davURLAsString];
userLogin = [[context activeUser] login];
principalURL
= [NSArray arrayWithObject: [NSString stringWithFormat: @"%@%@/", davURL,
userLogin]];
return [self _formalizePrincipalMatchResponse: principalURL];
}
- (void) _fillArrayWithPrincipalsOwnedBySelf: (NSMutableArray *) hrefs
{
NSArray *roles;
roles = [[context activeUser] rolesForObject: self inContext: context];
if ([roles containsObject: SoRole_Owner])
[hrefs addObject: [self davURLAsString]];
}
- (NSDictionary *)
_handlePrincipalMatchPrincipalProperty: (id <DOMElement>) child
{
NSMutableArray *hrefs;
NSDictionary *response;
hrefs = [NSMutableArray array];
[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;
}
/* actions */
- (id) DELETEAction: (id) _ctx
@@ -1073,6 +857,16 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
[self subclassResponsibility: _cmd];
}
- (void) setRoles: (NSArray *) roles
forUsers: (NSArray *) users
{
int count, max;
max = [users count];
for (count = 0; count < max; count++)
[self setRoles: roles forUser: [users objectAtIndex: count]];
}
- (void) removeAclsForUsers: (NSArray *) users
{
[self subclassResponsibility: _cmd];
@@ -1168,13 +962,8 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
- (NSString *) davURLAsString
{
NSURL *davURL;
SOGoSystemDefaults *sd;
davURL = [self davURL];
sd = [SOGoSystemDefaults sharedSystemDefaults];
return ([sd useRelativeURLs] ? [davURL path] : [davURL absoluteString]);
return [[container davURLAsString]
stringByAppendingFormat: @"%@", nameInContainer];
}
- (NSURL *) soURL
@@ -1315,14 +1104,53 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
[self nameInContainer]];
}
- (SOGoObject *) lookupObjectAtDAVUrl: (NSString *) davURL
{
NSString *appName, *baseSearchURL, *subString, *objectName;
NSRange searchRange;
NSArray *sogoObjects;
SOGoObject *currentObject, *resultObject;
int count, max;
resultObject = nil;
appName = [[context request] applicationName];
baseSearchURL = [NSString stringWithFormat: @"%@/dav/", appName];
searchRange = [davURL rangeOfString: baseSearchURL];
if (searchRange.location != NSNotFound)
{
subString = [davURL substringFromIndex: NSMaxRange (searchRange)];
currentObject = (SOGoObject *) [WOApplication application];
sogoObjects = [subString componentsSeparatedByString: @"/"];
max = [sogoObjects count];
for (count = 0; currentObject && count < max; count++)
{
objectName = [sogoObjects objectAtIndex: count];
if ([objectName length])
currentObject = [currentObject lookupName: objectName
inContext: context
acquire: NO];
}
resultObject = currentObject;
}
return resultObject;
}
- (NSArray *) davComplianceClassesInContext: (WOContext *) localContext
{
NSMutableArray *newClasses;
static NSMutableArray *newClasses = nil;
NSArray *selfClasses;
newClasses
= [NSMutableArray arrayWithArray:
[super davComplianceClassesInContext: localContext]];
[newClasses addObject: @"access-control"];
if (!newClasses)
{
newClasses
= [[super davComplianceClassesInContext: localContext] mutableCopy];
selfClasses = [NSArray arrayWithObjects: @"access-control",
@"calendar-access", @"calendar-schedule",
@"calendar-proxy", nil];
[newClasses addObjectsFromArray: selfClasses];
}
return newClasses;
}
@@ -1503,7 +1331,7 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
for (i = 0; i < [allUsers count]; i++)
{
[self setRoles: allRoles
forUser: [allUsers objectAtIndex: i]];
forUser: [allUsers objectAtIndex: i]];
}
result = @"";
}
@@ -1656,50 +1484,4 @@ SEL SOGoSelectorForPropertySetter (NSString *property)
return exception;
}
- (SOGoWebDAVValue *) davSupportedReportSet
{
NSDictionary *currentValue;
NSEnumerator *reportKeys;
NSMutableArray *reportSet;
NSString *currentKey;
reportSet = [NSMutableArray array];
reportKeys = [[reportMap allKeys] objectEnumerator];
while ((currentKey = [reportKeys nextObject]))
if ([self _reportSelector: currentKey])
{
currentValue = [currentKey asDavInvocation];
[reportSet addObject: davElementWithContent(@"report",
@"DAV:",
currentValue)];
}
return [davElementWithContent (@"supported-report-set", @"DAV:", reportSet)
asWebDAVValue];
}
@end /* SOGoObject */
@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

View File

@@ -35,6 +35,7 @@
#import <GDLContentStore/GCSFolderManager.h>
#import <GDLContentStore/NSURL+GCS.h>
#import <GDLAccess/EOAdaptorChannel.h>
#import <DOM/DOMProtocols.h>
#import <SaxObjC/XMLNamespaces.h>
#import "NSObject+DAV.h"
@@ -265,6 +266,7 @@ static SoSecurityManager *sm = nil;
- (NSException *) appendSubscribedSources
{
NSArray *subscribedReferences;
SOGoUser *ownerUser;
SOGoUserSettings *settings;
NSEnumerator *allKeys;
NSString *currentKey;
@@ -272,7 +274,8 @@ static SoSecurityManager *sm = nil;
error = nil; /* we ignore non-DB errors at this time... */
settings = [[context activeUser] userSettings];
ownerUser = [SOGoUser userWithLogin: [self ownerInContext: context]];
settings = [ownerUser userSettings];
subscribedReferences = [[settings objectForKey: nameInContainer]
objectForKey: @"SubscribedFolders"];
if ([subscribedReferences isKindOfClass: [NSArray class]])
@@ -357,16 +360,17 @@ static SoSecurityManager *sm = nil;
- (NSException *) initSubscribedSubFolders
{
NSString *login;
NSException *error;
SOGoUser *currentUser;
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])
currentUser = [context activeUser];
if (!subscribedSubFolders
&& ([[currentUser login] isEqualToString: owner]
|| [currentUser isSuperUser]))
{
subscribedSubFolders = [NSMutableDictionary new];
error = [self appendSubscribedSources];

View File

@@ -38,8 +38,6 @@ extern NSString *SOGoRole_FolderViewer;
extern NSString *SOGoRole_AuthorizedSubscriber;
extern NSString *SOGoRole_None;
extern NSString *SOGoRole_FreeBusy;
extern NSString *SOGoRole_FreeBusyLookup;
extern NSString *SOGoMailRole_SeenKeeper;
extern NSString *SOGoMailRole_Writer;
@@ -51,6 +49,8 @@ extern NSString *SOGoMailRole_Administrator;
extern NSString *SOGoCalendarRole_Organizer;
extern NSString *SOGoCalendarRole_Participant;
extern NSString *SOGoCalendarRole_FreeBusyReader;
extern NSString *SOGoCalendarRole_PublicViewer;
extern NSString *SOGoCalendarRole_PublicDAndTViewer;
extern NSString *SOGoCalendarRole_PublicModifier;
@@ -74,6 +74,8 @@ extern NSString *SOGoPerm_DeleteObject;
extern NSString *SOGoPerm_ReadAcls;
extern NSString *SOGoPerm_FreeBusyLookup;
extern NSString *SOGoCalendarPerm_ReadFreeBusy;
extern NSString *SOGoCalendarPerm_ViewWholePublicRecords;
extern NSString *SOGoCalendarPerm_ViewDAndTOfPublicRecords;
extern NSString *SOGoCalendarPerm_ModifyPublicRecords;

View File

@@ -34,18 +34,13 @@ NSString *SOGoRole_FolderEraser = @"FolderEraser";
NSString *SOGoRole_AuthorizedSubscriber = @"AuthorizedSubscriber";
NSString *SOGoRole_None = @"None";
NSString *SOGoRole_FreeBusy = @"FreeBusy"; /* for the "freebusy" special user
*/
NSString *SOGoRole_FreeBusyLookup = @"FreeBusyLookup"; /* for users that have
access to the
freebusy information
from a specific
calendar */
/* Calendar */
NSString *SOGoCalendarRole_Organizer = @"Organizer";
NSString *SOGoCalendarRole_Participant = @"Participant";
/* a special role that is given to all subscribed users */
NSString *SOGoCalendarRole_FreeBusyReader = @"FreeBusyReader";
NSString *SOGoCalendarRole_PublicViewer = @"PublicViewer";
NSString *SOGoCalendarRole_PublicDAndTViewer = @"PublicDAndTViewer";
NSString *SOGoCalendarRole_PublicModifier = @"PublicModifier";
@@ -73,10 +68,13 @@ NSString *SOGoMailRole_Administrator = @"MailAdministrator";
/* permissions */
NSString *SOGoPerm_AccessObject= @"Access Object";
NSString *SOGoPerm_DeleteObject= @"Delete Object";
NSString *SOGoPerm_ReadAcls = @"ReadAcls"; /* the equivalent of "read-acl" in
the WebDAV acls spec, which is
currently missing from SOPE */
NSString *SOGoPerm_FreeBusyLookup = @"FreeBusyLookup";
/* the equivalent of "read-acl" in the WebDAV acls spec, which is currently
missing from SOPE */
NSString *SOGoPerm_ReadAcls = @"ReadAcls";
NSString *SOGoCalendarPerm_ReadFreeBusy = @"Read FreeBusy";
NSString *SOGoCalendarPerm_ViewWholePublicRecords = @"ViewWholePublicRecords";
NSString *SOGoCalendarPerm_ViewDAndTOfPublicRecords = @"ViewDAndTOfPublicRecords";

View File

@@ -340,53 +340,6 @@
return _domainDefaults;
}
// - (SOGoUserDefaults *) userDefaults
// {
// if (!_defaults)
// {
// [SOGoUserDefaults defaultsForUser: login
// inDomain: [self domainName]]
// if (!SOGoUserProfileKlass)
// SOGoUserProfileKlass
// = NSClassFromString ([self userProfileClassName]);
// _defaults = [SOGoUserProfileKlass
// userProfileWithType: SOGoUserProfileTypeDefaults
// forUID: login];
// if (_defaults)
// {
// [_defaults retain];
// [_defaults fetchProfile];
// if ([_defaults values])
// {
// BOOL b;
// b = NO;
// if (![[_defaults stringForKey: @"MessageCheck"] length])
// {
// [_defaults setObject: defaultMessageCheck forKey: @"MessageCheck"];
// b = YES;
// }
// if (![[_defaults stringForKey: @"TimeZone"] length])
// {
// [_defaults setObject: [serverTimeZone name] forKey: @"TimeZone"];
// b = YES;
// }
// if (b)
// [_defaults synchronize];
// // See explanation in -language
// [self invalidateLanguage];
// }
// }
// }
// //else
// // NSLog(@"User defaults cache hit for %@", login);
// return _defaults;
// }
- (SOGoUserSettings *) userSettings
{
if (!_settings)
@@ -398,28 +351,6 @@
return _settings;
}
// - (void) invalidateLanguage
// {
// DESTROY(language);
// }
// - (NSString *) language
// {
// if (![language length])
// {
// language = [[self userDefaults] stringForKey: @"Language"];
// // This is a workaround until we handle the connection errors to the db
// // in a better way. It enables us to avoid retrieving the userDefaults
// // too many times when the DB is down, causing a huge delay.
// if (![language length])
// language = [SOGoUser language];
// [language retain];
// }
// return language;
// }
- (NSCalendarDate *) firstDayOfWeekForDate: (NSCalendarDate *) date
{
int offset;
@@ -650,9 +581,8 @@
- (SOGoUserFolder *) homeFolderInContext: (id) context
{
return [[WOApplication application] lookupName: [self login]
inContext: context
acquire: NO];
return [SOGoUserFolder objectWithName: login
inContainer: [WOApplication application]];
}
- (SOGoAppointmentFolders *) calendarsFolderInContext: (WOContext *) context

View File

@@ -58,6 +58,8 @@
- (id) mailAccountsFolder: (NSString *) _key
inContext: (WOContext *) _ctx;
- (NSArray *) davPrincipalURL;
@end
#endif /* __SOGo_SOGoUserFolder_H__ */

View File

@@ -57,6 +57,7 @@
#import "SOGoSystemDefaults.h"
#import "SOGoUser.h"
#import "WORequest+SOGo.h"
#import "WOResponse+SOGo.h"
#import "SOGoUserFolder.h"
@@ -366,12 +367,7 @@
id <DOMDocument> document;
r = [context response];
[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 prepareDAVResponse];
[r appendContentString: @"<D:multistatus xmlns:D=\"DAV:\""
@" xmlns=\"urn:ietf:params:xml:ns:inverse-dav\">"];
@@ -473,10 +469,7 @@
NSString *content;
r = [queryContext response];
[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 prepareDAVResponse];
document = [[context request] contentAsDOMDocument];
content = [self _davUsersFromQuery: document];
@@ -484,10 +477,7 @@
{
[r setStatus: 207];
if ([content length])
{
[r appendContentString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[r appendContentString: content];
}
[r appendContentString: content];
}
else
[r setStatus: 400];

View File

@@ -23,14 +23,29 @@
#ifndef SOGOUSERSETTINGS_H
#define SOGOUSERSETTINGS_H
@class NSString;
#import "SOGoDefaultsSource.h"
@class NSArray;
@class NSString;
@interface SOGoUserSettings : SOGoDefaultsSource
+ (SOGoUserSettings *) settingsForUser: (NSString *) userId;
/* the calendars that we publish to our proxy subscribers */
- (void) setProxiedCalendars: (NSArray *) proxiedCalendars;
- (NSArray *) proxiedCalendars;
/* the users that we have subscribed us as a proxy to our calendars */
- (void) setCalendarProxyUsers: (NSArray *) proxyUsers
withWriteAccess: (BOOL) writeAccess;
- (NSArray *) calendarProxyUsersWithWriteAccess: (BOOL) writeAccess;
/* the users that have subscribed us as a proxy to their calendars */
- (void) setCalendarProxySubscriptionUsers: (NSArray *) subscriptionUsers
withWriteAccess: (BOOL) writeAccess;
- (NSArray *) calendarProxySubscriptionUsersWithWriteAccess: (BOOL) writeAccess;
@end
#endif /* SOGOUSERSETTINGS_H */

View File

@@ -20,6 +20,7 @@
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
#import "SOGoUserProfile.h"
@@ -54,4 +55,65 @@ static Class SOGoUserProfileKlass = Nil;
return ud;
}
/* the calendars that we publish to our proxy subscribers */
- (void) setProxiedCalendars: (NSArray *) proxiedCalendars
{
[self setObject: proxiedCalendars forKey: @"ProxiedCalendars"];
}
- (NSArray *) proxiedCalendars
{
NSArray *proxiedCalendars;
proxiedCalendars = [self arrayForKey: @"ProxiedCalendars"];
if (!proxiedCalendars)
proxiedCalendars = [NSArray arrayWithObject: @"personal"];
return proxiedCalendars;
}
/* the users that we have subscribed us as a proxy to our calendars */
- (void) setCalendarProxyUsers: (NSArray *) proxyUsers
withWriteAccess: (BOOL) writeAccess
{
NSString *key;
key = [NSString stringWithFormat: @"CalendarProxy%@Users",
(writeAccess ? @"Read" : @"Write")];
[self setObject: proxyUsers forKey: key];
}
- (NSArray *) calendarProxyUsersWithWriteAccess: (BOOL) writeAccess
{
NSString *key;
key = [NSString stringWithFormat: @"CalendarProxy%@Users",
(writeAccess ? @"Read" : @"Write")];
return [self arrayForKey: key];
}
/* the users that have subscribed us as a proxy to their calendars */
- (void) setCalendarProxySubscriptionUsers: (NSArray *) subscriptionUsers
withWriteAccess: (BOOL) writeAccess
{
NSString *key;
key = [NSString stringWithFormat: @"CalendarProxy%@SubscriptionUsers",
(writeAccess ? @"Read" : @"Write")];
[self setObject: subscriptionUsers forKey: key];
}
- (NSArray *) calendarProxySubscriptionUsersWithWriteAccess: (BOOL) writeAccess
{
NSString *key;
key = [NSString stringWithFormat: @"CalendarProxy%@SubscriptionUsers",
(writeAccess ? @"Read" : @"Write")];
return [self arrayForKey: key];
}
@end

View File

@@ -116,18 +116,20 @@ static NSNumber *yesObject = nil;
identifier = [parentPermission keysWithFormat: @"{%{ns}}%{method}"];
parentEntry = [aclTree objectForKey: identifier];
if (!parentEntry)
[self warnWithFormat: @"parent entry '%@' does not exist in DAV"
@" permissions table", identifier];
children = [parentEntry objectForKey: @"children"];
if (!children)
if (parentEntry)
{
children = [NSMutableArray new];
[parentEntry setObject: children forKey: @"children"];
[children release];
children = [parentEntry objectForKey: @"children"];
if (!children)
{
children = [NSMutableArray array];
[parentEntry setObject: children forKey: @"children"];
}
[children addObject: newEntry];
[newEntry setObject: parentEntry forKey: @"parent"];
}
[children addObject: newEntry];
[newEntry setObject: parentEntry forKey: @"parent"];
else
[self errorWithFormat: @"parent entry '%@' does not exist in DAV"
@" permissions table", identifier];
}
- (void) registerDAVPermission: (NSDictionary *) davPermission

View File

@@ -29,6 +29,8 @@
@interface WOResponse (SOGoSOPEUtilities)
- (void) prepareDAVResponse;
- (void) appendDAVError: (NSDictionary *) errorCondition;
@end

View File

@@ -31,6 +31,16 @@
@implementation WOResponse (SOGoSOPEUtilities)
- (void) prepareDAVResponse
{
[self setStatus: 207];
[self setContentEncoding: NSUTF8StringEncoding];
[self setHeader: @"text/xml; charset=\"utf-8\"" forKey: @"content-type"];
[self setHeader: @"no-cache" forKey: @"pragma"];
[self setHeader: @"no-cache" forKey: @"cache-control"];
[self appendContentString:@"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"];
}
- (void) appendDAVError: (NSDictionary *) errorCondition
{
NSDictionary *error;
@@ -38,8 +48,6 @@
error = davElementWithContent (@"error", XMLNS_WEBDAV, errorCondition);
[self setStatus: 403];
[self appendContentString: @"<?xml version=\"1.0\""
@" encoding=\"utf-8\"?>\r\n"];
[self appendContentString: [error asWebDavStringWithNamespaces: nil]];
}

124
Tests/test-ical.py Executable file
View File

@@ -0,0 +1,124 @@
#!/usr/bin/python
from config import hostname, port, username, password, subscriber_username
import unittest
import webdavlib
class iCalTest(unittest.TestCase):
def testPrincipalCollectionSet(self):
"""principal-collection-set: 'DAV' header must be returned with iCal 4"""
client = webdavlib.WebDAVClient(hostname, port, username, password)
resource = '/SOGo/dav/%s/' % username
# NOT iCal4
propfind = webdavlib.WebDAVPROPFIND(resource,
["{DAV:}principal-collection-set"],
0)
propfind.xpath_namespace = { "D": "DAV:" }
client.execute(propfind)
self.assertEquals(propfind.response["status"], 207)
headers = propfind.response["headers"]
self.assertFalse(headers.has_key("dav"),
"DAV header must not be returned when user-agent is NOT iCal 4")
# iCal4
propfind = webdavlib.WebDAVPROPFIND(resource,
["{DAV:}principal-collection-set"],
0)
client.user_agent = "DAVKit/4.0.1 (730); CalendarStore/4.0.1 (973); iCal/4.0.1 (1374); Mac OS X/10.6.2 (10C540)"
propfind.xpath_namespace = { "D": "DAV:" }
client.execute(propfind)
self.assertEquals(propfind.response["status"], 207)
headers = propfind.response["headers"]
self.assertTrue(headers.has_key("dav"),
"DAV header must be returned when user-agent is iCal 4")
expectedDAVClasses = ["1", "2", "access-control", "calendar-access",
"calendar-schedule", "calendar-proxy"]
davClasses = [x.strip() for x in headers["dav"].split(",")]
for davClass in expectedDAVClasses:
self.assertTrue(davClass in davClasses,
"DAV class '%s' not found" % davClass)
def _setMemberSet(self, owner, members, perm):
resource = '/SOGo/dav/%s/calendar-proxy-%s/' % (owner, perm)
membersHref = [ { "{DAV:}href": '/SOGo/dav/%s/' % x }
for x in members ]
props = { "{DAV:}group-member-set": membersHref }
proppatch = webdavlib.WebDAVPROPPATCH(resource, props)
client = webdavlib.WebDAVClient(hostname, port, username, password)
client.user_agent = "DAVKit/4.0.1 (730); CalendarStore/4.0.1 (973); iCal/4.0.1 (1374); Mac OS X/10.6.2 (10C540)"
proppatch.xpath_namespace = { "D": "DAV:" }
client.execute(proppatch)
self.assertEquals(proppatch.response["status"], 207,
"failure (%s) setting '%s' permission for '%s' on %s's calendars"
% (proppatch.response["status"], perm,
"', '".join(members), owner))
def _getMembership(self, user):
resource = '/SOGo/dav/%s/' % user
propfind = webdavlib.WebDAVPROPFIND(resource,
["{DAV:}group-membership"], 0)
client = webdavlib.WebDAVClient(hostname, port, username, password)
client.user_agent = "DAVKit/4.0.1 (730); CalendarStore/4.0.1 (973); iCal/4.0.1 (1374); Mac OS X/10.6.2 (10C540)"
propfind.xpath_namespace = { "D": "DAV:" }
client.execute(propfind)
hrefs = propfind.xpath_evaluate("/D:multistatus/D:response/D:propstat/D:prop/D:group-membership/D:href")
members = [x.childNodes[0].nodeValue for x in hrefs]
return members
def _getProxyFor(self, user, perm):
resource = '/SOGo/dav/%s/' % user
prop = "{http://calendarserver.org/ns/}calendar-proxy-%s-for" % perm
propfind = webdavlib.WebDAVPROPFIND(resource, [prop], 0)
client = webdavlib.WebDAVClient(hostname, port, username, password)
client.user_agent = "DAVKit/4.0.1 (730); CalendarStore/4.0.1 (973); iCal/4.0.1 (1374); Mac OS X/10.6.2 (10C540)"
propfind.xpath_namespace = { "D": "DAV:", "n1": "http://calendarserver.org/ns/" }
client.execute(propfind)
hrefs = propfind.xpath_evaluate("/D:multistatus/D:response/D:propstat/D:prop/n1:calendar-proxy-%s-for/D:href"
% perm)
members = [x.childNodes[0].nodeValue[len("/SOGo/dav/"):-1] for x in hrefs]
return members
def testCalendarProxy(self):
self._setMemberSet(username, [], "read")
self._setMemberSet(username, [], "write")
self._setMemberSet(subscriber_username, [], "read")
self._setMemberSet(subscriber_username, [], "write")
self.assertEquals([], self._getMembership(username),
"'%s' must have no membership"
% username)
self.assertEquals([], self._getMembership(subscriber_username),
"'%s' must have no membership"
% subscriber_username)
self.assertEquals([], self._getProxyFor(username, "read"),
"'%s' must not be a proxy for anyone" % username)
self.assertEquals([], self._getProxyFor(username, "write"),
"'%s' must not be a proxy for anyone" % username)
self.assertEquals([], self._getProxyFor(subscriber_username, "read"),
"'%s' must not be a proxy for anyone" % subscriber_username)
self.assertEquals([], self._getProxyFor(subscriber_username, "write"),
"'%s' must not be a proxy for anyone" % subscriber_username)
for perm in ("read", "write"):
for users in ((username, subscriber_username),
(subscriber_username, username)):
self._setMemberSet(users[0], [users[1]], perm)
membership = self._getMembership(users[1])
self.assertEquals(['/SOGo/dav/%s/calendar-proxy-%s/'
% (users[0], perm)],
membership,
"'%s' must have %s access to %s's calendars"
% (users[1], perm, users[0]))
proxyFor = self._getProxyFor(users[1], perm)
self.assertEquals([users[0]], proxyFor,
"'%s' expected to be %s proxy for %s: %s"
% (users[1], perm, users[0], proxyFor))
if __name__ == "__main__":
unittest.main()

View File

@@ -5,6 +5,24 @@ from config import hostname, port, username, password
import unittest
import webdavlib
def fetchUserInfo(login):
client = webdavlib.WebDAVClient(hostname, port, username, password)
resource = "/SOGo/dav/%s/" % login
propfind = webdavlib.WebDAVPROPFIND(resource,
["displayname",
"{urn:ietf:params:xml:ns:caldav}calendar-user-address-set"],
0)
propfind.xpath_namespace = { "D": "DAV:",
"C": "urn:ietf:params:xml:ns:caldav" }
client.execute(propfind)
assert(propfind.response["status"] == 207)
name_nodes = propfind.xpath_evaluate('/D:multistatus/D:response/D:propstat/D:prop/D:displayname',
None)
email_nodes = propfind.xpath_evaluate('/D:multistatus/D:response/D:propstat/D:prop/C:calendar-user-address-set/D:href',
None)
return (name_nodes[0].childNodes[0].nodeValue, email_nodes[0].childNodes[0].nodeValue)
class WebDAVTest(unittest.TestCase):
def testPrincipalCollectionSet(self):
"""property: 'principal-collection-set' on collection object"""
@@ -15,18 +33,18 @@ class WebDAVTest(unittest.TestCase):
0)
propfind.xpath_namespace = { "D": "DAV:" }
client.execute(propfind)
assert(propfind.response["status"] == 207)
self.assertEquals(propfind.response["status"], 207)
nodes = propfind.xpath_evaluate('/D:multistatus/D:response/D:propstat/D:prop/D:principal-collection-set/D:href',
None)
responseHref = nodes[0].childNodes[0].nodeValue
if responseHref[0:4] == "http":
self.assertEquals("http://%s%s" % (hostname, resource), responseHref,
"{DAV:}principal-collection-set returned %s instead of '%s'"
self.assertEquals("http://%s/SOGo/dav/" % hostname, responseHref,
"{DAV:}principal-collection-set returned %s instead of 'http../SOGo/dav/'"
% ( responseHref, resource ))
else:
self.assertEquals(resource, responseHref,
"{DAV:}principal-collection-set returned %s instead of '%s'"
% ( responseHref, resource ))
self.assertEquals("/SOGo/dav/", responseHref,
"{DAV:}principal-collection-set returned %s instead of '/SOGo/dav/'"
% responseHref)
def testPrincipalCollectionSet2(self):
"""property: 'principal-collection-set' on non-collection object"""
@@ -37,11 +55,11 @@ class WebDAVTest(unittest.TestCase):
0)
propfind.xpath_namespace = { "D": "DAV:" }
client.execute(propfind)
assert(propfind.response["status"] == 207)
self.assertEquals(propfind.response["status"], 207)
nodes = propfind.xpath_evaluate('/D:multistatus/D:response/D:propstat/D:prop/D:principal-collection-set/D:href',
None)
responseHref = nodes[0].childNodes[0].nodeValue
expectedHref = '/SOGo/dav/%s/' % username
expectedHref = '/SOGo/dav/'
if responseHref[0:4] == "http":
self.assertEquals("http://%s%s" % (hostname, expectedHref), responseHref,
"{DAV:}principal-collection-set returned %s instead of '%s'"
@@ -51,5 +69,101 @@ class WebDAVTest(unittest.TestCase):
"{DAV:}principal-collection-set returned %s instead of '%s'"
% ( responseHref, expectedHref ))
def _testPropfindURL(self, resource):
resourceWithSlash = resource[-1] == '/'
client = webdavlib.WebDAVClient(hostname, port, username, password)
propfind = webdavlib.WebDAVPROPFIND(resource,
["{DAV:}displayname", "{DAV:}resourcetype"],
1)
propfind.xpath_namespace = { "D": "DAV:" }
client.execute(propfind)
self.assertEquals(propfind.response["status"], 207)
nodes = propfind.xpath_evaluate('/D:multistatus/D:response',
None)
for node in nodes:
responseHref = propfind.xpath_evaluate('D:href', node)[0].childNodes[0].nodeValue
hasSlash = responseHref[-1] == '/'
resourcetypes = \
propfind.xpath_evaluate('D:propstat/D:prop/D:resourcetype',
node)[0].childNodes
isCollection = len(resourcetypes) > 0
if isCollection:
self.assertEquals(hasSlash, resourceWithSlash,
"failure with href '%s' while querying '%s'"
% (responseHref, resource))
else:
self.assertEquals(hasSlash, False,
"failure with href '%s' while querying '%s'"
% (responseHref, resource))
def testPropfindURL(self):
"""propfind: ensure various NSURL work-arounds"""
# a collection without /
self._testPropfindURL('/SOGo/dav/%s' % username)
# a collection with /
self._testPropfindURL('/SOGo/dav/%s/' % username)
# a non-collection
self._testPropfindURL('/SOGo/dav/%s/freebusy.ifb' % username)
## REPORT
# http://tools.ietf.org/html/rfc3253.html#section-3.8
def testExpandProperty(self):
"""expand-property"""
client = webdavlib.WebDAVClient(hostname, port, username, password)
resource = '/SOGo/dav/%s/' % username
userInfo = fetchUserInfo(username)
query_props = {"owner": { "href": resource,
"displayname": userInfo[0]},
"principal-collection-set": { "href": "/SOGo/dav/",
"displayname": "SOGo"}}
query = webdavlib.WebDAVExpandProperty(resource, query_props.keys(),
["displayname"])
client.execute(query)
self.assertEquals(query.response["status"], 207)
topResponse = query.xpath_evaluate('/D:multistatus/D:response')[0]
topHref = query.xpath_evaluate('D:href', topResponse)[0]
self.assertEquals(resource, topHref.childNodes[0].nodeValue)
for query_prop in query_props.keys():
propResponse = query.xpath_evaluate('D:propstat/D:prop/D:%s'
% query_prop, topResponse)[0]
# <?xml version="1.0" encoding="utf-8"?>
# <D:multistatus xmlns:D="DAV:">
# <D:response>
# <D:href>/SOGo/dav/wsourdeau/</D:href>
# <D:propstat>
# <D:prop>
# <D:owner>
# <D:response>
# <D:href>/SOGo/dav/wsourdeau/</D:href>
# <D:propstat>
# <D:prop>
# <D:displayname>Wolfgang Sourdeau</D:displayname>
# </D:prop>
# <D:status>HTTP/1.1 200 OK</D:status>
# </D:propstat>
# </D:response>
# </D:owner>
propHref = query.xpath_evaluate('D:response/D:href',
propResponse)[0]
self.assertEquals(query_props[query_prop]["href"],
propHref.childNodes[0].nodeValue,
"'%s', href mismatch: exp. '%s', got '%s'"
% (query_prop,
query_props[query_prop]["href"],
propHref.childNodes[0].nodeValue))
propDisplayname = query.xpath_evaluate('D:response/D:propstat/D:prop/D:displayname',
propResponse)[0]
self.assertEquals(query_props[query_prop]["displayname"],
propDisplayname.childNodes[0].nodeValue,
"'%s', displayname mismatch: exp. '%s', got '%s'"
% (query_prop,
query_props[query_prop]["displayname"],
propDisplayname.nodeValue))
if __name__ == "__main__":
unittest.main()

View File

@@ -210,6 +210,21 @@ class WebDAVPROPFIND(WebDAVQuery):
if properties is not None and len(properties) > 0:
self._initProperties(properties)
class WebDAVPROPPATCH(WebDAVQuery):
method = "PROPPATCH"
# <x0:propertyupdate xmlns:x1="urn:ietf:params:xml:ns:caldav" xmlns:x0="DAV:"><x0:set><x0:prop>
def __init__(self, url, properties):
WebDAVQuery.__init__(self, url, None)
self.top_node = _WD_XMLTreeElement("propertyupdate")
set_node = _WD_XMLTreeElement("set")
self.top_node.append(set_node)
prop_node = _WD_XMLTreeElement("prop")
set_node.append(prop_node)
prop_node.appendSubtree(self, properties)
class WebDAVMOVE(WebDAVQuery):
method = "MOVE"
destination = None
@@ -314,6 +329,39 @@ class WebDAVSyncQuery(WebDAVREPORT):
if properties is not None and len(properties) > 0:
self._initProperties(properties)
class WebDAVExpandProperty(WebDAVREPORT):
def _parseTag(self, tag):
result = []
cb = tag.find("}")
if cb > -1:
result.append(tag[cb+1:])
result.append(tag[1:cb])
else:
result.append(tag)
result.append("DAV:")
return result;
def _propElement(self, tag):
parsedTag = self._parseTag(tag)
parameters = { "name": parsedTag[0] }
if len(parsedTag) > 1:
parameters["namespace"] = parsedTag[1]
return _WD_XMLTreeElement("property", parameters)
def __init__(self, url, query_properties, properties):
WebDAVQuery.__init__(self, url)
self.top_node = _WD_XMLTreeElement("expand-property")
for query_tag in query_properties:
property_query = self._propElement(query_tag)
self.top_node.append(property_query)
for tag in properties:
property = self._propElement(tag)
property_query.append(property)
class MailDAVMailQuery(WebDAVREPORT):
def __init__(self, url, properties, filters = None,
sort = None, ascending = True):
@@ -392,6 +440,11 @@ class _WD_XMLNS_MGR:
return newTag
class _WD_XMLTreeElement:
typeNum = type(0)
typeStr = type("")
typeList = type([])
typeDict = type({})
def __init__(self, tag, attributes = {}):
self.tag = tag
self.children = []
@@ -400,6 +453,26 @@ class _WD_XMLTreeElement:
def append(self, child):
self.children.append(child)
def appendSubtree(self, query, subtree):
if type(subtree) == self.typeNum:
strValue = "%d" % subtree
textNode = _WD_XMLTreeTextNode(strValue)
self.append(textNode)
elif type(subtree) == self.typeStr:
textNode = _WD_XMLTreeTextNode(subtree)
self.append(textNode)
elif type(subtree) == self.typeList:
for x in subtree:
self.appendSubtree(query, x)
elif type(subtree) == self.typeDict:
for x in subtree.keys():
tag = query.render_tag(x)
node = _WD_XMLTreeElement(tag)
node.appendSubtree(query, subtree[x])
self.append(node)
else:
None
def render(self, ns_text = None):
text = "<" + self.tag

View File

@@ -512,6 +512,9 @@ vtodo_class2 = "(Tarefa Confidencial)";
"Synchronize" = "Synchronize";
"Tag:" = "Marca:";
"iCal Delegation" = "iCal Delegation";
"Shared when account is delegated" = "Shared when account is delegated";
"Display" = "Display";
"Show alarms" = "Show alarms";
"Show tasks" = "Show tasks";

View File

@@ -512,6 +512,9 @@ vtodo_class2 = "(Důvěrný úkol)";
"Synchronize" = "Synchronize";
"Tag:" = "Štítek:";
"iCal Delegation" = "iCal Delegation";
"Shared when account is delegated" = "Shared when account is delegated";
"Display" = "Display";
"Show alarms" = "Show alarms";
"Show tasks" = "Show tasks";

View File

@@ -512,6 +512,9 @@ vtodo_class2 = "(Vertrouwelijke taak)";
"Synchronize" = "Synchronize";
"Tag:" = "Markering:";
"iCal Delegation" = "iCal Delegation";
"Shared when account is delegated" = "Shared when account is delegated";
"Display" = "Display";
"Show alarms" = "Show alarms";
"Show tasks" = "Show tasks";

View File

@@ -512,6 +512,9 @@ vtodo_class2 = "(Confidential task)";
"Synchronize" = "Synchronize";
"Tag:" = "Tag:";
"iCal Delegation" = "iCal Delegation";
"Shared when account is delegated" = "Shared when account is delegated";
"Display" = "Display";
"Show alarms" = "Show alarms";
"Show tasks" = "Show tasks";

View File

@@ -512,6 +512,9 @@ vtodo_class2 = "(Tâche confidentielle)";
"Synchronize" = "Synchroniser";
"Tag:" = "Label :";
"iCal Delegation" = "Délégation iCal";
"Shared when account is delegated" = "Inclure cet agenda lors de la délégation";
"Display" = "Affichage";
"Show alarms" = "Afficher les alarmes";
"Show tasks" = "Afficher les tâches";

View File

@@ -38,7 +38,6 @@ SchedulerUI_OBJC_FILES = \
UIxCalendarSelector.m \
UIxAppointmentEditor.m \
UIxTaskEditor.m \
UIxCalDateLabel.m \
UIxDatePicker.m \
UIxTimeDateControl.m \
UIxCalParticipationStatusView.m \

View File

@@ -512,6 +512,9 @@ vtodo_class2 = "(Vertrauliche Aufgabe)";
"Synchronize" = "Synchronize";
"Tag:" = "Tag:";
"iCal Delegation" = "iCal Delegation";
"Shared when account is delegated" = "Shared when account is delegated";
"Display" = "Display";
"Show alarms" = "Show alarms";
"Show tasks" = "Show tasks";

View File

@@ -512,6 +512,9 @@ vtodo_class2 = "(Bizalmas feladat)";
"Synchronize" = "Synchronize";
"Tag:" = "Cimke:";
"iCal Delegation" = "iCal Delegation";
"Shared when account is delegated" = "Shared when account is delegated";
"Display" = "Display";
"Show alarms" = "Show alarms";
"Show tasks" = "Show tasks";

View File

@@ -512,6 +512,9 @@ vtodo_class2 = "(Attività confidenziale)";
"Synchronize" = "Synchronize";
"Tag:" = "Etichetta:";
"iCal Delegation" = "iCal Delegation";
"Shared when account is delegated" = "Shared when account is delegated";
"Display" = "Display";
"Show alarms" = "Show alarms";
"Show tasks" = "Show tasks";

View File

@@ -512,6 +512,9 @@ vtodo_class2 = "(Confidential task)";
"Synchronize" = "Synchronize";
"Tag:" = "Tag:";
"iCal Delegation" = "iCal Delegation";
"Shared when account is delegated" = "Shared when account is delegated";
"Display" = "Display";
"Show alarms" = "Show alarms";
"Show tasks" = "Show tasks";

View File

@@ -512,6 +512,9 @@ vtodo_class2 = "(Tarea confidencial)";
"Synchronize" = "Synchronize";
"Tag:" = "Redacción:";
"iCal Delegation" = "iCal Delegation";
"Shared when account is delegated" = "Shared when account is delegated";
"Display" = "Display";
"Show alarms" = "Show alarms";
"Show tasks" = "Show tasks";

View File

@@ -512,6 +512,9 @@ vtodo_class2 = "(Konfidentiell uppgift)";
"Synchronize" = "Synkronisera";
"Tag:" = "Etikett:";
"iCal Delegation" = "iCal Delegation";
"Shared when account is delegated" = "Shared when account is delegated";
"Display" = "Visa";
"Show alarms" = "Visa alarm";
"Show tasks" = "Visa uppgifter";

View File

@@ -51,4 +51,7 @@
- (NSString *) calendarSyncTag;
- (void) setCalendarSyncTag: (NSString *) newTag;
- (BOOL) isProxied;
- (void) setIsProxied: (BOOL) isProxied;
@end

View File

@@ -149,6 +149,17 @@
[calendar setSyncTag: newTag];
}
/* DAV: calendar-proxy protocol */
- (BOOL) isProxied
{
return [calendar isProxied];
}
- (void) setIsProxied: (BOOL) isProxied
{
[calendar setIsProxied: isProxied];
}
- (BOOL) showCalendarAlarms
{
return [calendar showCalendarAlarms];

View File

@@ -512,6 +512,9 @@ vtodo_class2 = "(Tasg gyhoeddus)";
"Synchronize" = "Synchronize";
"Tag:" = "Tag:";
"iCal Delegation" = "iCal Delegation";
"Shared when account is delegated" = "Shared when account is delegated";
"Display" = "Display";
"Show alarms" = "Show alarms";
"Show tasks" = "Show tasks";

View File

@@ -15,7 +15,7 @@
var:value="calendarID"/>
<div id="propertiesView">
<fieldset>
<legend><var:string label:value="Properties" /></legend>
<legend><var:string label:value="Properties"/></legend>
<div><span class="label"><var:string label:value="Name:"/></span
><span class="content"><input type="text"
name="calendarName" id="calendarName"
@@ -40,47 +40,65 @@
/></span></div>
</fieldset>
<fieldset>
<legend><var:string label:value="Synchronization" /></legend>
<div><span class="label"><var:string label:value="Synchronize" /></span
><span class="content"><input type="checkbox" const:class="checkBox"
<legend><var:string label:value="Synchronization"/></legend>
<div><label
><input type="checkbox" const:class="checkBox"
name="synchronizeCalendar"
id="synchronizeCalendar"
var:checked="synchronizeCalendar"
var:disabled="mustSynchronize" /></span
></div>
<var:if condition="mustSynchronize" const:negate="YES"><div><span class="label"><var:string
label:value="Tag:"/></span><span class="content"><input type="text"
var:disabled="mustSynchronize"
/><var:string label:value="Synchronize"
/></label></div>
<var:if condition="mustSynchronize" const:negate="YES"
><div><label><var:string
label:value="Tag:"/><input type="text"
name="calendarSyncTag"
id="calendarSyncTag"
class="textField"
var:value="calendarSyncTag"
/><var:if condition="synchronizeCalendar"><input type="hidden"
name="originalCalendarSyncTag"
id="originalCalendarSyncTag"
var:value="originalCalendarSyncTag"
/></var:if><input type="hidden"
name="allCalendarSyncTags"
id="allCalendarSyncTags"
var:value="allCalendarSyncTags"
/></span></div></var:if>
/></label
><var:if condition="synchronizeCalendar"><input type="hidden"
name="originalCalendarSyncTag"
id="originalCalendarSyncTag"
var:value="originalCalendarSyncTag"
/></var:if><input type="hidden"
name="allCalendarSyncTags"
id="allCalendarSyncTags"
var:value="allCalendarSyncTags"
/></div></var:if>
</fieldset>
<var:if condition="isWebCalendar" const:negate="YES"
><fieldset>
<legend><var:string label:value="iCal Delegation"/></legend>
<div><label
><input type="checkbox" const:class="checkBox" id="isProxied"
var:checked="isProxied"
/><var:string label:value="Shared when account is delegated"
/></label></div>
</fieldset></var:if>
<fieldset>
<legend><var:string label:value="Display" /></legend>
<div><span class="label"><var:string label:value="Show alarms" /></span
><span class="content"><input type="checkbox" const:class="checkBox"
id="showCalendarAlarms" var:checked="showCalendarAlarms" /></span
></div>
<div><span class="label"><var:string label:value="Show tasks" /></span
><span class="content"><input type="checkbox" const:class="checkBox"
id="showCalendarTasks" var:checked="showCalendarTasks" /></span
></div>
<legend><var:string label:value="Display"/></legend>
<div><label
><input type="checkbox" const:class="checkBox"
id="showCalendarAlarms" var:checked="showCalendarAlarms"
/><var:string label:value="Show alarms"/></label></div>
<div><label
><input type="checkbox" const:class="checkBox"
id="showCalendarTasks" var:checked="showCalendarTasks"
/><var:string label:value="Show tasks"
/></label></div>
</fieldset>
<div id="buttons">
<a href="#" class="button actionButton" id="okButton"
name="okButton">
<span><var:string label:value="OK" /></span></a>
<span><var:string label:value="OK"/></span></a>
<a href="#" class="button" id="cancelButton" name="cancelButton">
<span><var:string label:value="Cancel" /></span></a>
<span><var:string label:value="Cancel"/></span></a>
</div>
</div>
</form>

View File

@@ -1891,16 +1891,17 @@ function onCalendarModify(event) {
var url = ApplicationBaseURL + calendarID + "/properties";
var windowID = sanitizeWindowName(calendarID + " properties");
var width = 310;
var height = 270;
var height = 310;
var isWebCalendar = false;
if (UserSettings['Calendar']
&& UserSettings['Calendar']['WebCalendars']) {
var webCalendars = UserSettings['Calendar']['WebCalendars'];
var realID = calendarID.substr (1, calendarID.length - 1);
if (webCalendars[realID])
height = 290;
isWebCalendar = true;
}
if (calendarID == "/personal")
height = 250;
if (isWebCalendar || calendarID == "/personal")
height -= 25;
var properties = window.open(url, windowID,
"width="+width+",height="+height+",resizable=0");