sogo/Protocols/iCalHTTP/SOGoICalFilePublish.m
Wolfgang Sourdeau 14993bccb0 Monotone-Parent: 8268efeb15753e2949b09378244938f0b6bb08a8
Monotone-Revision: c0c1c6cf72acfbec9c8a2b4e7eeef2f801f49e54

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2006-09-29T18:31:20
Monotone-Branch: ca.inverse.sogo
2006-09-29 18:31:20 +00:00

318 lines
8 KiB
Objective-C

/*
Copyright (C) 2004 SKYRIX Software AG
This file is part of OpenGroupware.org.
OGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
OGo 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 Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#include <NGObjWeb/WODirectAction.h>
@interface SOGoICalFilePublish : WODirectAction
{
}
@end
#include "SOGoICalHTTPHandler.h"
#include <SoObjects/Appointments/SOGoAppointmentFolder.h>
#include <SoObjects/Appointments/SOGoAppointmentObject.h>
#include <NGCards/NGCards.h>
#include <SaxObjC/SaxObjC.h>
#include "common.h"
@implementation SOGoICalFilePublish
static BOOL debugOn = YES;
static id<NSObject,SaxXMLReader> parser = nil;
static SaxObjectDecoder *sax = nil;
+ (void)initialize {
if (parser == nil) {
parser = [[[SaxXMLReaderFactory standardXMLReaderFactory]
createXMLReaderForMimeType:@"text/calendar"]
retain];
if (parser == nil)
NSLog(@"ERROR: did not find a parser for text/calendar!");
}
if (sax == nil) {
sax = [[SaxObjectDecoder alloc] initWithMappingNamed:@"NGCards"];
if (sax == nil)
NSLog(@"ERROR: could not create the iCal SAX handler!");
}
[parser setContentHandler:sax];
[parser setErrorHandler:sax];
}
/* clientObject */
- (id)clientObject {
return [[super clientObject] aptFolderInContext:[self context]];
}
/* change sets */
- (void)extractNewVEvents:(NSArray **)_new updatedVEvents:(NSArray **)_updated
andDeletedUIDs:(NSArray **)_deleted
whenComparingOldUIDs:(NSArray *)_old withNewVEvents:(NSArray *)_pub
{
unsigned i, count;
if (_new != NULL ) *_new = nil;
if (_updated != NULL ) *_updated = nil;
if (_deleted != NULL ) *_deleted = nil;
/* scan old array for changes */
for (i = 0, count = [_old count]; i < count; i++) {
id obj;
obj = [_old objectAtIndex:i];
if (![obj isNotNull]) continue;
if ([_pub containsObject:obj]) {
/* updated object, in both sets */
if (_updated == NULL) continue;
if (*_updated == nil) *_updated = [NSMutableArray arrayWithCapacity:16];
[(NSMutableArray *)*_updated addObject:obj];
}
else {
/* deleted object, only in old set */
if (_deleted == NULL) continue;
if (*_deleted == nil) *_deleted = [NSMutableArray arrayWithCapacity:4];
[(NSMutableArray *)*_deleted addObject:obj];
}
}
/* scan new array for new objects */
for (i = 0, count = [_pub count]; i < count; i++) {
id obj;
obj = [_pub objectAtIndex:i];
if (![obj isNotNull]) continue;
if ([_old containsObject:obj]) /* already processed */
continue;
if (_new == NULL) continue;
if (*_new == nil) *_new = [NSMutableArray arrayWithCapacity:16];
[(NSMutableArray *)*_new addObject:obj];
}
}
/* operation */
- (NSException *)publishVToDos:(NSArray *)_todos {
// TODO: work on tasks folder?
if ([_todos count] == 0)
return nil;
#if 1
return [NSException exceptionWithHTTPStatus:501 /* Not Implemented */
reason:@"server does not support vtodo PUTs!"];
#else
return nil /* means: OK */;
#endif
}
- (NSException *)writeNewVEvents:(NSArray *)_events {
SOGoAppointmentFolder *folder;
NSException *error;
unsigned i, count;
if ((folder = [self clientObject]) == nil) {
return [NSException exceptionWithHTTPStatus:404 /* Not Found */
reason:@"did not find clientObject?!"];
}
for (i = 0, count = [_events count]; i < count; i++) {
SOGoAppointmentObject *object;
iCalEvent *event;
NSString *ical;
event = [_events objectAtIndex:i];
ical = [event versitString];
if (![ical isNotNull] && ([ical length] == 0)) {
[self logWithFormat:@"ERROR: got no ical representation of event: %@",
event];
continue;
}
object = [folder lookupName:[event uid] inContext:[self context]
acquire:NO];
if (![object isNotNull]) {
// TODO: what to do?
[self logWithFormat:@"ERROR: could not lookup event: %@", [event uid]];
continue;
}
if ((error = [object saveContentString:ical]) != nil) /* failed, abort */
return error;
}
return nil; // TODO: fake OK
}
- (NSException *)updateVEvents:(NSArray *)_events {
if ([_events count] == 0)
return nil;
[self logWithFormat:@"TODO: should update: %@", _events];
return nil; // TODO: fake OK
}
- (NSException *)deleteUIDs:(NSArray *)_uids {
if ([_uids count] == 0)
return nil;
[self logWithFormat:@"TODO: should delete UIDs: %@",
[_uids componentsJoinedByString:@", "]];
return nil; // TODO: fake OK
}
- (NSException *)publishVEvents:(NSArray *)_events {
// TODO: extract UIDs and compare sets
NSException *ex;
NSArray *availUIDs;
NSArray *new, *updated, *deleted;
[self debugWithFormat:@"publish %d events ...", [_events count]];
/* find changeset */
availUIDs = [[self clientObject] toOneRelationshipKeys];
/* build changeset */
[self extractNewVEvents:&new updatedVEvents:&updated andDeletedUIDs:&deleted
whenComparingOldUIDs:availUIDs
withNewVEvents:_events];
/* process */
if ([new count] > 0) {
if ((ex = [self writeNewVEvents:new]) != nil)
return ex;
}
if ([updated count] > 0) {
if ((ex = [self updateVEvents:updated]) != nil)
return ex;
}
if ([deleted count] > 0) {
if ((ex = [self deleteUIDs:deleted]) != nil)
return ex;
}
return nil /* means: OK */;
}
- (NSException *)publishICalCalendar:(iCalCalendar *)_iCal {
NSException *ex;
[self debugWithFormat:@"publish iCalCalendar: %@", _iCal];
if ((ex = [self publishVEvents:[_iCal events]]) != nil)
return ex;
if ((ex = [self publishVToDos:[_iCal todos]]) != nil)
return ex;
return nil /* means: OK */;
}
- (NSException *)publishICalendarString:(NSString *)_ical {
NSException *ex;
id root;
[parser parseFromSource:_ical];
root = [[sax rootObject] retain]; /* retain to keep it around */
[sax reset];
if (![root isNotNull]) {
[self debugWithFormat:@"invalid iCal input: %@", _ical];
return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
reason:@"could not parse iCal input?!"];
}
if ([root isKindOfClass:[NSException class]])
return [root autorelease];
if ([root isKindOfClass:[iCalCalendar class]]) {
ex = [self publishICalCalendar:root];
}
else {
ex = [NSException exceptionWithHTTPStatus:501 /* Not Implemented */
reason:@"cannot deal with input"];
}
[root release]; root = nil;
return ex /* means: OK */;
}
/* responses */
- (WOResponse *)publishOkResponse {
WOResponse *r;
r = [[self context] response];
[r setStatus:200 /* OK */];
return r;
}
/* actions */
- (id)defaultAction {
/*
Note: Apple iCal.app submits no content-type!
*/
NSString *s;
s = [[[self context] request] contentAsString];
if ([s length] == 0) {
[self debugWithFormat:@"missing content!"];
return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
reason:@"missing iCalendar content"];
}
if ([s hasPrefix:@"BEGIN:VCALENDAR"]) {
NSException *e;
if ((e = [self publishICalendarString:s]) == nil)
return [self publishOkResponse];
return e;
}
[self debugWithFormat:@"ERROR: cannot process input: %@", s];
/* Fake successful publish ... */
return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
reason:@"invalid input format"];
}
/* debugging */
- (BOOL)isDebuggingEnabled {
return debugOn;
}
@end /* SOGoICalFilePublish */