mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-02-17 07:33:57 +00:00
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:
71
ChangeLog
71
ChangeLog
@@ -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.
|
||||
|
||||
|
||||
@@ -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
39
Main/SOGo+DAV.h
Normal 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
513
Main/SOGo+DAV.m
Normal 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
|
||||
60
Main/SOGo.m
60
Main/SOGo.m
@@ -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
4
NEWS
@@ -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)
|
||||
--------------------
|
||||
|
||||
@@ -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) */
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
43
SoObjects/Appointments/SOGoAppointmentInboxFolder.h
Normal file
43
SoObjects/Appointments/SOGoAppointmentInboxFolder.h
Normal 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 */
|
||||
84
SoObjects/Appointments/SOGoAppointmentInboxFolder.m
Normal file
84
SoObjects/Appointments/SOGoAppointmentInboxFolder.m
Normal 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
|
||||
@@ -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
|
||||
|
||||
36
SoObjects/Appointments/SOGoUser+Appointments.h
Normal file
36
SoObjects/Appointments/SOGoUser+Appointments.h
Normal 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 */
|
||||
74
SoObjects/Appointments/SOGoUser+Appointments.m
Normal file
74
SoObjects/Appointments/SOGoUser+Appointments.m
Normal 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
|
||||
@@ -32,10 +32,7 @@
|
||||
|
||||
- (NSArray *) davCalendarUserAddressSet;
|
||||
- (NSArray *) davCalendarHomeSet;
|
||||
- (NSArray *) davCalendarScheduleInboxURL;
|
||||
- (NSArray *) davCalendarScheduleOutboxURL;
|
||||
- (NSArray *) davDropboxHomeURL;
|
||||
- (NSArray *) davNotificationsURL;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
// };
|
||||
|
||||
@@ -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\">"];
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSCharacterSet.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -27,6 +27,13 @@
|
||||
|
||||
@class DOMElement;
|
||||
|
||||
@interface NGDOMElement (SOGo)
|
||||
|
||||
- (NSString *) asPropertyName;
|
||||
- (NSString *) asPropertyPropertyName;
|
||||
|
||||
@end
|
||||
|
||||
@interface NGDOMNodeWithChildren (SOGoDOMExtensions)
|
||||
|
||||
- (id <DOMNodeList>) childElementsWithTag: (NSString *) tagName;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
|
||||
@interface NSArray (SOGoArrayUtilities)
|
||||
|
||||
+ (id) arrayWithObject: (id) member
|
||||
repeatCount: (int) repeatCount;
|
||||
|
||||
- (id *) asPointersOfObjects;
|
||||
|
||||
- (NSString *) jsonRepresentation;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -34,6 +34,13 @@
|
||||
- (NSMutableDictionary *) asWebDAVTuple;
|
||||
- (NSMutableDictionary *) asWebDAVTupleWithContent: (id) content;
|
||||
|
||||
|
||||
- (NSString *) davMethodToObjC;
|
||||
- (NSString *) davSetterName;
|
||||
- (NSDictionary *) asDavInvocation;
|
||||
|
||||
- (NSString *) removeOutsideTags;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* NSSTRING_DAV_H */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -36,10 +36,6 @@
|
||||
|
||||
- (NSString *) urlWithoutParameters;
|
||||
|
||||
- (NSString *) davMethodToObjC;
|
||||
- (NSString *) davSetterName;
|
||||
- (NSDictionary *) asDavInvocation;
|
||||
|
||||
- (NSString *) stringByDetectingURLs;
|
||||
|
||||
- (NSString *) doubleQuotedString;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -58,6 +58,8 @@
|
||||
- (id) mailAccountsFolder: (NSString *) _key
|
||||
inContext: (WOContext *) _ctx;
|
||||
|
||||
- (NSArray *) davPrincipalURL;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __SOGo_SOGoUserFolder_H__ */
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
@interface WOResponse (SOGoSOPEUtilities)
|
||||
|
||||
- (void) prepareDAVResponse;
|
||||
|
||||
- (void) appendDAVError: (NSDictionary *) errorCondition;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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
124
Tests/test-ical.py
Executable 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()
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -38,7 +38,6 @@ SchedulerUI_OBJC_FILES = \
|
||||
UIxCalendarSelector.m \
|
||||
UIxAppointmentEditor.m \
|
||||
UIxTaskEditor.m \
|
||||
UIxCalDateLabel.m \
|
||||
UIxDatePicker.m \
|
||||
UIxTimeDateControl.m \
|
||||
UIxCalParticipationStatusView.m \
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -51,4 +51,7 @@
|
||||
- (NSString *) calendarSyncTag;
|
||||
- (void) setCalendarSyncTag: (NSString *) newTag;
|
||||
|
||||
- (BOOL) isProxied;
|
||||
- (void) setIsProxied: (BOOL) isProxied;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user