mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-07-11 03:15:10 +00:00
merge of '546031563b555a3095688a4ec17eeb5902aea9a1'
and 'dcb58071ad12a3e87d2a061647e9558e70bb9775' Monotone-Parent: 546031563b555a3095688a4ec17eeb5902aea9a1 Monotone-Parent: dcb58071ad12a3e87d2a061647e9558e70bb9775 Monotone-Revision: 77ae5931198ed270924efe912f3a0be9fe7a7a62 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-07-09T21:09:59 Monotone-Branch: ca.inverse.sogo
This commit is contained in:
@@ -1,3 +1,45 @@
|
||||
2008-07-09 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentFolders.m ([SOGoAppointmentFolders -davCreateCalendarCollection:newNameinContext:createContext]):
|
||||
new method that execute a "MKCALENDAR" request.
|
||||
|
||||
* SoObjects/SOGo/SOGoObject.m (SOGoSelectorForPropertyGetter): new
|
||||
function that returns the selector for the getter method for the
|
||||
specified property.
|
||||
(SOGoSelectorForPropertySetter): same as above for the setter
|
||||
method.
|
||||
|
||||
* SoObjects/SOGo/WORequest+SOGo.m ([WORequest
|
||||
-davPatchedPropertiesWithTopTag:topTag]): new category method to
|
||||
help with parsing properties modified or removed from PROPPATCH
|
||||
methods and alike.
|
||||
|
||||
* SoObjects/SOGo/SOGoContentObject.m ([SOGoContentObject
|
||||
-PUTAction:_ctx]): increment the version number only if the record
|
||||
is not new.
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
([SOGoAppointmentFolder -davCalendarFreeBusySet]): new method that
|
||||
returns the HREF to the calendars involved in the user's freebusy.
|
||||
([SOGoAppointmentFolder
|
||||
-setDavCalendarFreeBusySet:newFreeBusySet]): new method that
|
||||
PRETENDS to modify the list of calendars mentionned above.
|
||||
([SOGoAppointmentFolder
|
||||
-caldavEventRequest:eventwithContent:iCalStringfrom:originatorto:recipients]):
|
||||
add support for the "CANCEL" method.
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentObject.m
|
||||
([SOGoAppointmentObject -postCalDAVEventCancelTo:recipients]): new
|
||||
method that implements support for the CalDAV "CANCEL" method for
|
||||
POSTs.
|
||||
|
||||
* SoObjects/SOGo/SOGoContentObject.m ([SOGoContentObject
|
||||
-PUTAction:_ctx]): the version passed to
|
||||
saveContentString:baseVersion: is the current value of "version".
|
||||
If the operation is succesful, the version is incremented from
|
||||
here since no refresh is done from GCS. The returned etag has thus
|
||||
now the correct value.
|
||||
|
||||
2008-07-09 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* UI/MailerUI/UIxMailMainFrame.m ([WOActionResults
|
||||
|
||||
@@ -1741,6 +1741,21 @@ Index: sope-core/NGExtensions/EOExt.subproj/EOGlobalID+Ext.m
|
||||
#import <EOControl/EOGlobalID.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
Index: sope-core/NGStreams/GNUmakefile.preamble
|
||||
===================================================================
|
||||
--- sope-core/NGStreams/GNUmakefile.preamble (révision 1623)
|
||||
+++ sope-core/NGStreams/GNUmakefile.preamble (copie de travail)
|
||||
@@ -1,7 +1,10 @@
|
||||
# compilation settings
|
||||
|
||||
+MACHCPU = $(shell echo $$MACHTYPE | cut -f 1 -d '-')
|
||||
+
|
||||
libNGStreams_INCLUDE_DIRS += \
|
||||
-I$(GNUSTEP_TARGET_CPU)/$(GNUSTEP_TARGET_OS) \
|
||||
+ -I./$(MACHCPU)/$(GNUSTEP_TARGET_OS) \
|
||||
-INGStreams \
|
||||
-I../NGExtensions \
|
||||
-I..
|
||||
Index: sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.h
|
||||
===================================================================
|
||||
--- sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.h (révision 1623)
|
||||
@@ -1990,7 +2005,7 @@ Index: sope-appserver/NGObjWeb/DAVPropMap.plist
|
||||
"{DAV:}locktoken" = davLockToken;
|
||||
"{DAV:}activelock" = davActiveLock;
|
||||
// TODO: non-standard?, also used by WebDrive
|
||||
@@ -120,12 +126,29 @@
|
||||
@@ -120,12 +126,31 @@
|
||||
"{http://ucb.openoffice.org/dav/props/}IsRemoveable" = isOOoRemoveable;
|
||||
"{http://ucb.openoffice.org/dav/props/}IsVolume" = isOOoVolume;
|
||||
"{http://ucb.openoffice.org/dav/props/}TargetURL" = davOOoTargetURL;
|
||||
@@ -2016,12 +2031,14 @@ Index: sope-appserver/NGObjWeb/DAVPropMap.plist
|
||||
"{urn:ietf:params:xml:ns:caldav}calendar-home-set" = davCalendarHomeSet;
|
||||
+ "{urn:ietf:params:xml:ns:caldav}calendar-user-address-set" =
|
||||
+ davCalendarUserAddressSet;
|
||||
+ "{urn:ietf:params:xml:ns:caldav}calendar-free-busy-set" =
|
||||
+ davCalendarFreeBusySet;
|
||||
+ "{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL" = davCalendarScheduleInboxURL;
|
||||
+ "{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL" = davCalendarScheduleOutboxURL;
|
||||
"{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set" =
|
||||
davCalendarComponentSet;
|
||||
"{urn:ietf:params:xml:ns:caldav}supported-calendar-data" =
|
||||
@@ -138,13 +161,13 @@
|
||||
@@ -138,13 +163,13 @@
|
||||
"{urn:ietf:params:xml:ns:carddav}addressbook-description" = davDescription;
|
||||
|
||||
/* Apple CalServer */
|
||||
@@ -2077,6 +2094,95 @@ Index: sope-appserver/NGObjWeb/WebDAV/SoObjectWebDAVDispatcher.m
|
||||
return [self httpException:400 /* invalid request */
|
||||
reason:@"XML entity expected for WebDAV REPORT."];
|
||||
}
|
||||
@@ -1601,8 +1601,60 @@
|
||||
/* CalDAV */
|
||||
|
||||
- (id)doMKCALENDAR:(WOContext *)_ctx {
|
||||
- return [self httpException:405 /* method not allowed */
|
||||
- reason:@"CalDAV calendar creation not yet implemented."];
|
||||
+ SoSecurityManager *sm;
|
||||
+ NSException *e;
|
||||
+ NSString *pathInfo;
|
||||
+
|
||||
+ pathInfo = [_ctx pathInfo];
|
||||
+ if (![pathInfo isNotEmpty]) {
|
||||
+ /* MKCALENDAR target already exists ... */
|
||||
+ WOResponse *r;
|
||||
+
|
||||
+ [self logWithFormat:@"MKCALENDAR target exists !"];
|
||||
+
|
||||
+ r = [_ctx response];
|
||||
+ [r setStatus:405 /* method not allowed */];
|
||||
+ [r appendContentString:@"calendar collection already exists !"];
|
||||
+ return r;
|
||||
+ }
|
||||
+
|
||||
+ /* check permissions */
|
||||
+
|
||||
+ sm = [_ctx soSecurityManager];
|
||||
+ e = [sm validatePermission:SoPerm_AddFolders
|
||||
+ onObject:self->object
|
||||
+ inContext:_ctx];
|
||||
+ if (e != nil) return e;
|
||||
+
|
||||
+ /* check whether all the parent collections are available */
|
||||
+ if ([pathInfo rangeOfString:@"/"].length > 0) {
|
||||
+ return [self httpException:409 /* Conflict */
|
||||
+ reason:
|
||||
+ @"invalid WebDAV MKCALENDAR request, first create all "
|
||||
+ @"parent collections !"];
|
||||
+ }
|
||||
+
|
||||
+ /* check whether the object supports creating collections */
|
||||
+
|
||||
+ if (![self->object respondsToSelector:
|
||||
+ @selector(davCreateCalendarCollection:inContext:)]) {
|
||||
+ /* Note: this should never happen, as this is implemented on NSObject */
|
||||
+
|
||||
+ [self logWithFormat:@"MKCALENDAR: object '%@' path-info '%@'",
|
||||
+ self->object, pathInfo];
|
||||
+ return [self httpException:405 /* not allowed */
|
||||
+ reason:
|
||||
+ @"this object cannot create a new calendar collection with MKCALENDAR"];
|
||||
+ }
|
||||
+
|
||||
+ if ((e = [self->object davCreateCalendarCollection:pathInfo inContext:_ctx])) {
|
||||
+ [self debugWithFormat:@"creation of calendar collection '%@' failed: %@",
|
||||
+ pathInfo, e];
|
||||
+ return e;
|
||||
+ }
|
||||
+
|
||||
+ [self debugWithFormat:@"created calendar collection."];
|
||||
+ return [NSNumber numberWithBool:YES];
|
||||
}
|
||||
|
||||
/* DAV access control lists */
|
||||
Index: sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m
|
||||
===================================================================
|
||||
--- sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m (révision 1623)
|
||||
+++ sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m (copie de travail)
|
||||
@@ -277,7 +277,8 @@
|
||||
ok = [self renderLockToken:_object inContext:_ctx];
|
||||
break;
|
||||
case 'M':
|
||||
- if ([m isEqualToString:@"MKCOL"])
|
||||
+ if ([m isEqualToString:@"MKCOL"]
|
||||
+ || [m isEqualToString:@"MKCALENDAR"])
|
||||
ok = [self renderMkColResult:_object inContext:_ctx];
|
||||
else if ([m isEqualToString:@"MOVE"]) {
|
||||
ok = [self renderStatusResult:_object
|
||||
Index: sope-appserver/NGObjWeb/WebDAV/SoObject+SoDAV.h
|
||||
===================================================================
|
||||
--- sope-appserver/NGObjWeb/WebDAV/SoObject+SoDAV.h (révision 1623)
|
||||
+++ sope-appserver/NGObjWeb/WebDAV/SoObject+SoDAV.h (copie de travail)
|
||||
@@ -62,6 +62,7 @@
|
||||
properties:(NSDictionary *)_props
|
||||
inContext:(id)_ctx;
|
||||
- (NSException *)davCreateCollection:(NSString *)_name inContext:(id)_ctx;
|
||||
+- (NSException *)davCreateCalendarCollection:(NSString *)_name inContext:(id)_ctx;
|
||||
|
||||
- (NSException *)davMoveToTargetObject:(id)_target newName:(NSString *)_name
|
||||
inContext:(id)_ctx;
|
||||
Index: sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m
|
||||
===================================================================
|
||||
--- sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m (révision 1623)
|
||||
@@ -2292,6 +2398,20 @@ Index: sope-appserver/NGObjWeb/SoObjects/SoObject.m
|
||||
}
|
||||
}
|
||||
|
||||
Index: sope-appserver/NGObjWeb/SoObjects/SoObject+Traversal.m
|
||||
===================================================================
|
||||
--- sope-appserver/NGObjWeb/SoObjects/SoObject+Traversal.m (révision 1623)
|
||||
+++ sope-appserver/NGObjWeb/SoObjects/SoObject+Traversal.m (copie de travail)
|
||||
@@ -195,7 +195,8 @@
|
||||
isCreateIfMissingMethod = YES;
|
||||
else if ([m isEqualToString:@"PROPPATCH"])
|
||||
isCreateIfMissingMethod = YES;
|
||||
- else if ([m isEqualToString:@"MKCOL"])
|
||||
+ else if ([m isEqualToString:@"MKCOL"]
|
||||
+ || [m isEqualToString:@"MKCALENDAR"])
|
||||
/* this one is strictly creating */
|
||||
isCreateMethod = YES;
|
||||
// TODO: the following are only create-if-missing on the target!
|
||||
Index: sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m
|
||||
===================================================================
|
||||
--- sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m (révision 1623)
|
||||
@@ -2317,3 +2437,54 @@ Index: sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m
|
||||
@end /* WOHttpAdaptor */
|
||||
|
||||
@implementation WOCoreApplication(SimpleParserSelection)
|
||||
Index: sope-appserver/NGObjWeb/Defaults.plist
|
||||
===================================================================
|
||||
--- sope-appserver/NGObjWeb/Defaults.plist (révision 1623)
|
||||
+++ sope-appserver/NGObjWeb/Defaults.plist (copie de travail)
|
||||
@@ -216,7 +216,7 @@
|
||||
SoWebDAVDisableCrossHostMoveCheck = NO;
|
||||
|
||||
SoWebDAVDefaultAllowMethods = (
|
||||
- GET, HEAD, POST, OPTIONS, MKCOL, DELETE, PUT,
|
||||
+ GET, HEAD, POST, OPTIONS, MKCOL, MKCALENDAR, DELETE, PUT,
|
||||
LOCK, UNLOCK, COPY, MOVE
|
||||
/* , NOTIFY, POLL, SUBSCRIBE, UNSUBSCRIBE */
|
||||
);
|
||||
@@ -224,6 +224,7 @@
|
||||
SoWebDAVDetectionMethods = (
|
||||
OPTIONS,
|
||||
MKCOL,
|
||||
+ MKCALENDAR,
|
||||
PROPFIND,
|
||||
PROPPATCH,
|
||||
DELETE,
|
||||
Index: sope-appserver/NGObjWeb/NGHttp/NGHttpRequest.h
|
||||
===================================================================
|
||||
--- sope-appserver/NGObjWeb/NGHttp/NGHttpRequest.h (révision 1623)
|
||||
+++ sope-appserver/NGObjWeb/NGHttp/NGHttpRequest.h (copie de travail)
|
||||
@@ -62,6 +62,10 @@
|
||||
/* RFC 3253 (DeltaV) */
|
||||
NGHttpMethod_REPORT,
|
||||
NGHttpMethod_VERSION_CONTROL,
|
||||
+ /* RFC 4791 (CalDAV) */
|
||||
+ NGHttpMethod_MKCALENDAR,
|
||||
+ /* http://ietfreport.isoc.org/idref/draft-daboo-carddav/ (CardDAV) */
|
||||
+ NGHttpMethod_MKADDRESSBOOK,
|
||||
NGHttpMethod_last
|
||||
} NGHttpMethod;
|
||||
|
||||
Index: sope-appserver/NGObjWeb/NGHttp/NGHttpRequest.m
|
||||
===================================================================
|
||||
--- sope-appserver/NGObjWeb/NGHttp/NGHttpRequest.m (révision 1623)
|
||||
+++ sope-appserver/NGObjWeb/NGHttp/NGHttpRequest.m (copie de travail)
|
||||
@@ -59,6 +59,10 @@
|
||||
/* RFC 3253 (DeltaV) */
|
||||
@"REPORT",
|
||||
@"VERSION-CONTROL",
|
||||
+ /* RFC 4791 (CalDAV) */
|
||||
+ @"MKCALENDAR",
|
||||
+ /* http://ietfreport.isoc.org/idref/draft-daboo-carddav/ (CardDAV) */
|
||||
+ @"MKADDRESSBOOK",
|
||||
nil
|
||||
};
|
||||
|
||||
|
||||
@@ -891,36 +891,6 @@ static Class sogoAppointmentFolderKlass = Nil;
|
||||
#warning we should use the EOFetchSpecification for that!!! (see doPROPFIND:)
|
||||
|
||||
#warning components in calendar-data query are ignored
|
||||
static inline SEL
|
||||
_selectorForProperty (NSString *property)
|
||||
{
|
||||
static NSMutableDictionary *methodMap = nil;
|
||||
SEL propSel;
|
||||
NSValue *propPtr;
|
||||
NSDictionary *map;
|
||||
NSString *methodName;
|
||||
|
||||
if (!methodMap)
|
||||
methodMap = [NSMutableDictionary new];
|
||||
propPtr = [methodMap objectForKey: property];
|
||||
if (propPtr)
|
||||
propSel = [propPtr pointerValue];
|
||||
else
|
||||
{
|
||||
map = [sogoAppointmentFolderKlass defaultWebDAVAttributeMap];
|
||||
methodName = [map objectForKey: property];
|
||||
if (methodName)
|
||||
{
|
||||
propSel = NSSelectorFromString (methodName);
|
||||
if (propSel)
|
||||
[methodMap setObject: [NSValue valueWithPointer: propSel]
|
||||
forKey: property];
|
||||
}
|
||||
}
|
||||
|
||||
return propSel;
|
||||
}
|
||||
|
||||
- (NSString *) _nodeTagForProperty: (NSString *) property
|
||||
{
|
||||
NSString *namespace, *nodeName, *nsRep;
|
||||
@@ -984,7 +954,7 @@ _selectorForProperty (NSString *property)
|
||||
currentValue = values;
|
||||
while (*currentProperty)
|
||||
{
|
||||
methodSel = _selectorForProperty (*currentProperty);
|
||||
methodSel = SOGoSelectorForPropertyGetter (*currentProperty);
|
||||
if (methodSel && [sogoObject respondsToSelector: methodSel])
|
||||
*currentValue = [[sogoObject performSelector: methodSel]
|
||||
stringByEscapingXMLString];
|
||||
@@ -1304,6 +1274,32 @@ _selectorForProperty (NSString *property)
|
||||
return error;
|
||||
}
|
||||
|
||||
- (SOGoWebDAVValue *) davCalendarFreeBusySet
|
||||
{
|
||||
NSEnumerator *subFolders;
|
||||
SOGoAppointmentFolder *currentFolder;
|
||||
NSMutableArray *response;
|
||||
SOGoWebDAVValue *responseValue;
|
||||
|
||||
response = [NSMutableArray new];
|
||||
subFolders = [[container subFolders] objectEnumerator];
|
||||
while ((currentFolder = [subFolders nextObject]))
|
||||
[response addObject: davElementWithContent (@"href", XMLNS_WEBDAV,
|
||||
[currentFolder davURL])];
|
||||
responseValue = [davElementWithContent (@"calendar-free-busy-set", XMLNS_CALDAV, response)
|
||||
asWebDAVValue];
|
||||
[response release];
|
||||
|
||||
return responseValue;
|
||||
}
|
||||
|
||||
/* This method is ignored but we must return a success value. */
|
||||
- (NSException *) setDavCalendarFreeBusySet: (NSString *) newFreeBusySet
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (void) _appendComponentProperties: (NSString **) properties
|
||||
matchingFilters: (NSArray *) filters
|
||||
toResponse: (WOResponse *) response
|
||||
@@ -1751,7 +1747,9 @@ _selectorForProperty (NSString *property)
|
||||
if ([method isEqualToString: @"REQUEST"])
|
||||
elements = [apt postCalDAVEventRequestTo: recipients];
|
||||
else if ([method isEqualToString: @"REPLY"])
|
||||
elements = [apt postCalDAVEventReplyTo: recipients];
|
||||
elements = [apt postCalDAVEventReplyTo: recipients];
|
||||
else if ([method isEqualToString: @"CANCEL"])
|
||||
elements = [apt postCalDAVEventCancelTo: recipients];
|
||||
else
|
||||
elements = nil;
|
||||
|
||||
|
||||
@@ -21,8 +21,14 @@
|
||||
*/
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <NGObjWeb/WOContext.h>
|
||||
#import <NGObjWeb/WORequest+So.h>
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
|
||||
#import <SOGo/WORequest+SOGo.h>
|
||||
#import "SOGoAppointmentFolder.h"
|
||||
|
||||
#import "SOGoAppointmentFolders.h"
|
||||
@@ -44,6 +50,106 @@
|
||||
return [self labelForKey: @"Personal Calendar"];
|
||||
}
|
||||
|
||||
- (NSString *) _fetchPropertyWithName: (NSString *) propertyName
|
||||
inArray: (NSArray *) section
|
||||
{
|
||||
NSObject <DOMElement> *currentElement;
|
||||
NSString *currentName, *property;
|
||||
NSEnumerator *elements;
|
||||
NSObject <DOMNodeList> *values;
|
||||
|
||||
property = nil;
|
||||
|
||||
elements = [section objectEnumerator];
|
||||
while (!property && (currentElement = [elements nextObject]))
|
||||
{
|
||||
currentName = [NSString stringWithFormat: @"{%@}%@",
|
||||
[currentElement namespaceURI],
|
||||
[currentElement nodeName]];
|
||||
if ([currentName isEqualToString: propertyName])
|
||||
{
|
||||
values = [currentElement childNodes];
|
||||
if ([values length])
|
||||
property = [[values objectAtIndex: 0] nodeValue];
|
||||
}
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
#warning this method may be useful at a higher level
|
||||
#warning not all values are simple strings...
|
||||
- (NSException *) _applyMkCalendarProperties: (NSArray *) properties
|
||||
toObject: (SOGoObject *) newFolder
|
||||
{
|
||||
NSEnumerator *allProperties;
|
||||
NSObject <DOMElement> *currentProperty;
|
||||
NSObject <DOMNodeList> *values;
|
||||
NSString *value, *currentName;
|
||||
SEL methodSel;
|
||||
|
||||
allProperties = [properties objectEnumerator];
|
||||
while ((currentProperty = [allProperties nextObject]))
|
||||
{
|
||||
values = [currentProperty childNodes];
|
||||
if ([values length])
|
||||
{
|
||||
value = [[values objectAtIndex: 0] nodeValue];
|
||||
currentName = [NSString stringWithFormat: @"{%@}%@",
|
||||
[currentProperty namespaceURI],
|
||||
[currentProperty nodeName]];
|
||||
methodSel = SOGoSelectorForPropertySetter (currentName);
|
||||
if ([newFolder respondsToSelector: methodSel])
|
||||
[newFolder performSelector: methodSel
|
||||
withObject: value];
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSException *) davCreateCalendarCollection: (NSString *) newName
|
||||
inContext: (id) createContext
|
||||
{
|
||||
NSArray *subfolderNames, *setProperties;
|
||||
NSString *content, *newDisplayName;
|
||||
NSDictionary *properties;
|
||||
NSException *error;
|
||||
SOGoAppointmentFolder *newFolder;
|
||||
|
||||
subfolderNames = [self toManyRelationshipKeys];
|
||||
if ([subfolderNames containsObject: newName])
|
||||
{
|
||||
content = [NSString stringWithFormat:
|
||||
@"A collection named '%@' already exists.",
|
||||
newName];
|
||||
error = [NSException exceptionWithHTTPStatus: 403
|
||||
reason: content];
|
||||
}
|
||||
else
|
||||
{
|
||||
properties = [[createContext request]
|
||||
davPatchedPropertiesWithTopTag: @"mkcalendar"];
|
||||
setProperties = [properties objectForKey: @"set"];
|
||||
newDisplayName = [self _fetchPropertyWithName: @"{DAV:}displayname"
|
||||
inArray: setProperties];
|
||||
if (![newDisplayName length])
|
||||
newDisplayName = newName;
|
||||
error
|
||||
= [self newFolderWithName: newDisplayName andNameInContainer: newName];
|
||||
if (!error)
|
||||
{
|
||||
newFolder = [self lookupName: newName
|
||||
inContext: createContext
|
||||
acquire: NO];
|
||||
error = [self _applyMkCalendarProperties: setProperties
|
||||
toObject: newFolder];
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
#warning THIS CAUSES LIGHTNING TO FAIL (that is why its commented out)
|
||||
// - (NSArray *) davComplianceClassesInContext: (id)_ctx
|
||||
// {
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
|
||||
- (NSArray *) postCalDAVEventRequestTo: (NSArray *) recipients;
|
||||
- (NSArray *) postCalDAVEventReplyTo: (NSArray *) recipients;
|
||||
- (NSArray *) postCalDAVEventCancelTo: (NSArray *) recipients;
|
||||
|
||||
/* "iCal multifolder saves" */
|
||||
|
||||
|
||||
@@ -403,15 +403,34 @@
|
||||
return ex;
|
||||
}
|
||||
|
||||
- (NSDictionary *) _caldavSuccessCodeWithRecipient: (NSString *) recipient
|
||||
{
|
||||
NSMutableArray *element;
|
||||
NSDictionary *code;
|
||||
|
||||
element = [NSMutableArray new];
|
||||
[element addObject: davElementWithContent (@"recipient", XMLNS_CALDAV,
|
||||
recipient)];
|
||||
[element addObject: davElementWithContent (@"request-status",
|
||||
XMLNS_CALDAV,
|
||||
@"2.0;Success")];
|
||||
code = davElementWithContent (@"response", XMLNS_CALDAV,
|
||||
element);
|
||||
[element release];
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
- (NSArray *) postCalDAVEventRequestTo: (NSArray *) recipients
|
||||
{
|
||||
NSMutableArray *elements, *element;
|
||||
NSMutableArray *elements;
|
||||
NSEnumerator *recipientsEnum;
|
||||
NSString *recipient, *uid;
|
||||
iCalEvent *event;
|
||||
iCalPerson *person;
|
||||
|
||||
elements = [NSMutableArray array];
|
||||
|
||||
event = [self component: NO secure: NO];
|
||||
recipientsEnum = [recipients objectEnumerator];
|
||||
while ((recipient = [recipientsEnum nextObject]))
|
||||
@@ -427,15 +446,40 @@
|
||||
forOldObject: nil andNewObject: event
|
||||
toAttendees: [NSArray arrayWithObject: person]];
|
||||
[person release];
|
||||
element = [NSMutableArray new];
|
||||
[element addObject: davElementWithContent (@"recipient", XMLNS_CALDAV,
|
||||
recipient)];
|
||||
[element addObject: davElementWithContent (@"request-status",
|
||||
XMLNS_CALDAV,
|
||||
@"2.0;Success")];
|
||||
[elements addObject: davElementWithContent (@"response", XMLNS_CALDAV,
|
||||
element)];
|
||||
[element release];
|
||||
[elements
|
||||
addObject: [self _caldavSuccessCodeWithRecipient: recipient]];
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
- (NSArray *) postCalDAVEventCancelTo: (NSArray *) recipients
|
||||
{
|
||||
NSMutableArray *elements;
|
||||
NSEnumerator *recipientsEnum;
|
||||
NSString *recipient, *uid;
|
||||
iCalEvent *event;
|
||||
iCalPerson *person;
|
||||
|
||||
elements = [NSMutableArray array];
|
||||
|
||||
event = [self component: NO secure: NO];
|
||||
recipientsEnum = [recipients objectEnumerator];
|
||||
while ((recipient = [recipientsEnum nextObject]))
|
||||
if ([[recipient lowercaseString] hasPrefix: @"mailto:"])
|
||||
{
|
||||
person = [iCalPerson new];
|
||||
[person setValue: 0 to: recipient];
|
||||
uid = [person uid];
|
||||
if (uid)
|
||||
[self _removeEventFromUID: uid];
|
||||
#warning fix this when sendEmailUsing blabla has been cleaned up
|
||||
[self sendEMailUsingTemplateNamed: @"Deletion"
|
||||
forOldObject: nil andNewObject: event
|
||||
toAttendees: [NSArray arrayWithObject: person]];
|
||||
[person release];
|
||||
[elements
|
||||
addObject: [self _caldavSuccessCodeWithRecipient: recipient]];
|
||||
}
|
||||
|
||||
return elements;
|
||||
@@ -459,7 +503,7 @@
|
||||
|
||||
- (NSArray *) postCalDAVEventReplyTo: (NSArray *) recipients
|
||||
{
|
||||
NSMutableArray *elements, *element;
|
||||
NSMutableArray *elements;
|
||||
NSEnumerator *recipientsEnum;
|
||||
NSString *recipient, *uid, *eventUID;
|
||||
iCalEvent *event;
|
||||
@@ -493,15 +537,8 @@
|
||||
forOldObject: nil andNewObject: event
|
||||
toAttendees: [NSArray arrayWithObject: person]];
|
||||
[person release];
|
||||
element = [NSMutableArray new];
|
||||
[element addObject: davElementWithContent (@"recipient", XMLNS_CALDAV,
|
||||
recipient)];
|
||||
[element addObject: davElementWithContent (@"request-status",
|
||||
XMLNS_CALDAV,
|
||||
@"2.0;Success")];
|
||||
[elements addObject: davElementWithContent (@"response", XMLNS_CALDAV,
|
||||
element)];
|
||||
[element release];
|
||||
[elements
|
||||
addObject: [self _caldavSuccessCodeWithRecipient: recipient]];
|
||||
}
|
||||
|
||||
return elements;
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
andContent: (NSString *) newContent
|
||||
inContainer: (SOGoGCSFolder *) newContainer;
|
||||
- (id) initWithRecord: (NSDictionary *) objectRecord
|
||||
inContainer: (id) newContainer;
|
||||
+ (id) initWithName: (NSString *) newName
|
||||
inContainer: (SOGoGCSFolder *) newContainer;
|
||||
- (id) initWithName: (NSString *) newName
|
||||
andContent: (NSString *) newContent
|
||||
inContainer: (SOGoGCSFolder *) newContainer;
|
||||
|
||||
|
||||
@@ -53,13 +53,13 @@
|
||||
return newObject;
|
||||
}
|
||||
|
||||
+ (id) objectWithName: (NSDictionary *) objectRecord
|
||||
+ (id) objectWithName: (NSString *) newName
|
||||
andContent: (NSString *) newContent
|
||||
inContainer: (SOGoGCSFolder *) newContainer
|
||||
{
|
||||
SOGoContentObject *newObject;
|
||||
|
||||
newObject = [[self alloc] initWithName: objectRecord
|
||||
newObject = [[self alloc] initWithName: newName
|
||||
andContent: newContent
|
||||
inContainer: newContainer];
|
||||
[newObject autorelease];
|
||||
@@ -104,7 +104,7 @@
|
||||
}
|
||||
|
||||
- (id) initWithRecord: (NSDictionary *) objectRecord
|
||||
inContainer: (id) newContainer
|
||||
inContainer: (SOGoGCSFolder *) newContainer
|
||||
{
|
||||
NSString *newName;
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
|
||||
- (id) initWithName: (NSString *) newName
|
||||
andContent: (NSString *) newContent
|
||||
inContainer: (id) newContainer
|
||||
inContainer: (SOGoGCSFolder *) newContainer
|
||||
{
|
||||
if ((self = [self initWithName: newName inContainer: newContainer]))
|
||||
{
|
||||
@@ -233,88 +233,99 @@
|
||||
|
||||
- (id) PUTAction: (WOContext *) _ctx
|
||||
{
|
||||
WORequest *rq;
|
||||
NSException *error;
|
||||
WORequest *rq;
|
||||
NSException *error;
|
||||
unsigned int baseVersion;
|
||||
id etag, tmp;
|
||||
BOOL needsLocation;
|
||||
|
||||
if ((error = [self matchesRequestConditionInContext:_ctx]) != nil)
|
||||
return error;
|
||||
|
||||
rq = [_ctx request];
|
||||
|
||||
/* check whether its a request to the 'special' 'new' location */
|
||||
/*
|
||||
Note: this is kinda hack. The OGo ZideStore detects writes to 'new' as
|
||||
object creations and will assign a server side identifier. Most
|
||||
current GroupDAV clients rely on this behaviour, so we reproduce it
|
||||
here.
|
||||
A correct client would loop until it has a name which doesn't not
|
||||
yet exist (by using if-none-match).
|
||||
*/
|
||||
needsLocation = NO;
|
||||
tmp = [[self nameInContainer] stringByDeletingPathExtension];
|
||||
if ([tmp isEqualToString:@"new"]) {
|
||||
tmp = [self globallyUniqueObjectId];
|
||||
needsLocation = YES;
|
||||
|
||||
[self debugWithFormat:
|
||||
@"reassigned a new location for special new-location: %@", tmp];
|
||||
|
||||
/* kinda dangerous */
|
||||
ASSIGNCOPY(nameInContainer, tmp);
|
||||
}
|
||||
|
||||
/* determine base version from etag in if-match header */
|
||||
/*
|
||||
Note: The -matchesRequestConditionInContext: already checks whether the
|
||||
etag matches and returns an HTTP exception in case it doesn't.
|
||||
We retrieve the etag again here to _ensure_ a transactionally save
|
||||
commit.
|
||||
(between the check and the update a change could have been done)
|
||||
*/
|
||||
tmp = [rq headerForKey:@"if-match"];
|
||||
tmp = [self parseETagList:tmp];
|
||||
etag = nil;
|
||||
if ([tmp count] > 0) {
|
||||
if ([tmp count] > 1) {
|
||||
/*
|
||||
Note: we would have to attempt a save for _each_ of the etags being
|
||||
passed in! In practice most WebDAV clients submit exactly one
|
||||
etag.
|
||||
*/
|
||||
[self warnWithFormat:
|
||||
@"Got multiple if-match etags from client, only attempting to "
|
||||
@"save with the first: %@", tmp];
|
||||
}
|
||||
|
||||
etag = [tmp objectAtIndex:0];
|
||||
}
|
||||
baseVersion = ([etag length] > 0)
|
||||
? [etag unsignedIntValue]
|
||||
: 0 /* 0 means 'do not check' */;
|
||||
|
||||
/* attempt a save */
|
||||
id etag, tmp;
|
||||
BOOL needsLocation;
|
||||
WOResponse *response;
|
||||
|
||||
if ((error = [self saveContentString: [rq contentAsString]
|
||||
baseVersion: baseVersion]) != nil)
|
||||
return error;
|
||||
error = [self matchesRequestConditionInContext: _ctx];
|
||||
if (error)
|
||||
response = (WOResponse *) error;
|
||||
else
|
||||
{
|
||||
rq = [_ctx request];
|
||||
|
||||
/* setup response */
|
||||
/* check whether its a request to the 'special' 'new' location */
|
||||
/*
|
||||
Note: this is kinda hack. The OGo ZideStore detects writes to 'new' as
|
||||
object creations and will assign a server side identifier. Most
|
||||
current GroupDAV clients rely on this behaviour, so we reproduce it
|
||||
here.
|
||||
A correct client would loop until it has a name which doesn't not
|
||||
yet exist (by using if-none-match).
|
||||
*/
|
||||
needsLocation = NO;
|
||||
|
||||
tmp = [[self nameInContainer] stringByDeletingPathExtension];
|
||||
if ([tmp isEqualToString: @"new"])
|
||||
{
|
||||
tmp = [self globallyUniqueObjectId];
|
||||
needsLocation = YES;
|
||||
|
||||
[self debugWithFormat:
|
||||
@"reassigned a new location for special new-location: %@", tmp];
|
||||
|
||||
/* kinda dangerous */
|
||||
ASSIGNCOPY (nameInContainer, tmp);
|
||||
}
|
||||
|
||||
// TODO: this should be automatic in the SoDispatcher if we return nil?
|
||||
[[_ctx response] setStatus: 201 /* Created */];
|
||||
/* determine base version from etag in if-match header */
|
||||
/*
|
||||
Note: The -matchesRequestConditionInContext: already checks whether the
|
||||
etag matches and returns an HTTP exception in case it doesn't.
|
||||
We retrieve the etag again here to _ensure_ a transactionally save
|
||||
commit.
|
||||
(between the check and the update a change could have been done)
|
||||
*/
|
||||
tmp = [rq headerForKey: @"if-match"];
|
||||
tmp = [self parseETagList: tmp];
|
||||
etag = nil;
|
||||
if ([tmp count] > 0) {
|
||||
if ([tmp count] > 1) {
|
||||
/*
|
||||
Note: we would have to attempt a save for _each_ of the etags being
|
||||
passed in! In practice most WebDAV clients submit exactly one
|
||||
etag.
|
||||
*/
|
||||
[self warnWithFormat:
|
||||
@"Got multiple if-match etags from client, only attempting to "
|
||||
@"save with the first: %@", tmp];
|
||||
}
|
||||
|
||||
etag = [tmp objectAtIndex: 0];
|
||||
}
|
||||
baseVersion = (isNew ? 0 : version);
|
||||
|
||||
if ((etag = [self davEntityTag]) != nil)
|
||||
[[_ctx response] setHeader:etag forKey:@"etag"];
|
||||
/* attempt a save */
|
||||
|
||||
error = [self saveContentString: [rq contentAsString]
|
||||
baseVersion: baseVersion];
|
||||
if (error)
|
||||
response = (WOResponse *) error;
|
||||
else
|
||||
{
|
||||
if (!isNew)
|
||||
version++;
|
||||
response = [_ctx response];
|
||||
/* setup response */
|
||||
|
||||
if (needsLocation) {
|
||||
[[_ctx response] setHeader:[self baseURLInContext:_ctx]
|
||||
forKey:@"location"];
|
||||
}
|
||||
// TODO: this should be automatic in the SoDispatcher if we return nil?
|
||||
[response setStatus: 201 /* Created */];
|
||||
|
||||
return [_ctx response];
|
||||
etag = [self davEntityTag];
|
||||
if (etag)
|
||||
{
|
||||
[response setHeader: etag forKey: @"etag"];
|
||||
if (needsLocation)
|
||||
[response setHeader: [self baseURLInContext:_ctx]
|
||||
forKey: @"location"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/* E-Tags */
|
||||
|
||||
@@ -62,6 +62,9 @@
|
||||
|
||||
#define $(class) NSClassFromString(class)
|
||||
|
||||
SEL SOGoSelectorForPropertyGetter (NSString *property);
|
||||
SEL SOGoSelectorForPropertySetter (NSString *property);
|
||||
|
||||
@interface SOGoObject : NSObject
|
||||
{
|
||||
WOContext *context;
|
||||
|
||||
@@ -71,6 +71,64 @@ static BOOL kontactGroupDAV = YES;
|
||||
static BOOL sendACLAdvisories = NO;
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
return propSel;
|
||||
}
|
||||
|
||||
SEL SOGoSelectorForPropertySetter (NSString *property)
|
||||
{
|
||||
SEL propSel;
|
||||
NSValue *propPtr;
|
||||
NSDictionary *map;
|
||||
NSString *methodName;
|
||||
|
||||
if (!setterMap)
|
||||
setterMap = [NSMutableDictionary new];
|
||||
propPtr = [setterMap objectForKey: property];
|
||||
if (propPtr)
|
||||
propSel = [propPtr pointerValue];
|
||||
else
|
||||
{
|
||||
map = [SOGoObject defaultWebDAVAttributeMap];
|
||||
methodName = [map objectForKey: property];
|
||||
if (methodName)
|
||||
{
|
||||
propSel = NSSelectorFromString ([methodName davSetterName]);
|
||||
if (propSel)
|
||||
[setterMap setObject: [NSValue valueWithPointer: propSel]
|
||||
forKey: property];
|
||||
}
|
||||
}
|
||||
|
||||
return propSel;
|
||||
}
|
||||
|
||||
@implementation SOGoObject
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
|
||||
- (void) setBaseOCSPath: (NSString *) newOCSPath;
|
||||
|
||||
- (NSArray *) toManyRelationshipKeys;
|
||||
- (NSArray *) subFolders;
|
||||
|
||||
- (NSException *) newFolderWithName: (NSString *) name
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
@interface WORequest (SOGoSOPEUtilities)
|
||||
|
||||
- (BOOL) handledByDefaultHandler;
|
||||
- (NSDictionary *) davPatchedPropertiesWithTopTag: (NSString *) topTag;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -20,9 +20,13 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
|
||||
#import <NGObjWeb/SoObjectRequestHandler.h>
|
||||
#import <NGObjWeb/WOApplication.h>
|
||||
|
||||
#import <DOM/DOMProtocols.h>
|
||||
|
||||
#import "WORequest+SOGo.h"
|
||||
|
||||
@implementation WORequest (SOGoSOPEUtilities)
|
||||
@@ -33,4 +37,68 @@
|
||||
return (![requestHandlerKey isEqualToString: @"dav"]);
|
||||
}
|
||||
|
||||
- (NSArray *) _propertiesOfElement: (id <DOMElement>) startElement
|
||||
underTag: (NSString *) tag
|
||||
{
|
||||
id <DOMNodeList> list;
|
||||
id <DOMElement> tagElement;
|
||||
NSObject <DOMNode> *currentNode;
|
||||
NSMutableArray *properties;
|
||||
unsigned int count, max;
|
||||
|
||||
properties = nil;
|
||||
|
||||
list = [startElement getElementsByTagName: tag];
|
||||
if ([list length])
|
||||
{
|
||||
tagElement = [list objectAtIndex: 0];
|
||||
list = [tagElement getElementsByTagName: @"prop"];
|
||||
if ([list length])
|
||||
{
|
||||
tagElement = [list objectAtIndex: 0];
|
||||
properties = [NSMutableArray array];
|
||||
list = [tagElement childNodes];
|
||||
max = [list length];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
currentNode = [list objectAtIndex: count];
|
||||
if ([currentNode conformsToProtocol: @protocol (DOMElement)])
|
||||
[properties addObject: currentNode];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
- (NSDictionary *) davPatchedPropertiesWithTopTag: (NSString *) topTag
|
||||
{
|
||||
NSMutableDictionary *patchedProperties;
|
||||
NSArray *props;
|
||||
id <DOMDocument> element;
|
||||
id <DOMElement> startElement;
|
||||
NSObject <DOMNodeList> *list;
|
||||
|
||||
patchedProperties = nil;
|
||||
if (!topTag)
|
||||
topTag = @"propertyupdate";
|
||||
element = [self contentAsDOMDocument];
|
||||
list = [element getElementsByTagName: topTag];
|
||||
if ([list length])
|
||||
{
|
||||
startElement = [list objectAtIndex: 0];
|
||||
patchedProperties = [NSMutableDictionary dictionary];
|
||||
props = [self _propertiesOfElement: startElement
|
||||
underTag: @"set"];
|
||||
if (props)
|
||||
[patchedProperties setObject: props forKey: @"set"];
|
||||
props = [self _propertiesOfElement: startElement
|
||||
underTag: @"remove"];
|
||||
if (props)
|
||||
[patchedProperties setObject: props forKey: @"remove"];
|
||||
}
|
||||
|
||||
return patchedProperties;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user