2 Copyright (C) 2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include <NGObjWeb/WODirectAction.h>
24 @interface SOGoICalFilePublish : WODirectAction
30 #include "SOGoICalHTTPHandler.h"
31 #include <SoObjects/Appointments/SOGoAppointmentFolder.h>
32 #include <SoObjects/Appointments/SOGoAppointmentObject.h>
33 #include <NGCards/NGCards.h>
34 #include <SaxObjC/SaxObjC.h>
37 @implementation SOGoICalFilePublish
39 static BOOL debugOn = YES;
40 static id<NSObject,SaxXMLReader> parser = nil;
41 static SaxObjectDecoder *sax = nil;
45 parser = [[[SaxXMLReaderFactory standardXMLReaderFactory]
46 createXMLReaderForMimeType:@"text/calendar"]
49 NSLog(@"ERROR: did not find a parser for text/calendar!");
52 sax = [[SaxObjectDecoder alloc] initWithMappingNamed:@"NGCards"];
54 NSLog(@"ERROR: could not create the iCal SAX handler!");
57 [parser setContentHandler:sax];
58 [parser setErrorHandler:sax];
64 return [[super clientObject] aptFolderInContext:[self context]];
69 - (void)extractNewVEvents:(NSArray **)_new updatedVEvents:(NSArray **)_updated
70 andDeletedUIDs:(NSArray **)_deleted
71 whenComparingOldUIDs:(NSArray *)_old withNewVEvents:(NSArray *)_pub
75 if (_new != NULL ) *_new = nil;
76 if (_updated != NULL ) *_updated = nil;
77 if (_deleted != NULL ) *_deleted = nil;
79 /* scan old array for changes */
81 for (i = 0, count = [_old count]; i < count; i++) {
84 obj = [_old objectAtIndex:i];
85 if (![obj isNotNull]) continue;
87 if ([_pub containsObject:obj]) {
88 /* updated object, in both sets */
89 if (_updated == NULL) continue;
90 if (*_updated == nil) *_updated = [NSMutableArray arrayWithCapacity:16];
91 [(NSMutableArray *)*_updated addObject:obj];
94 /* deleted object, only in old set */
95 if (_deleted == NULL) continue;
96 if (*_deleted == nil) *_deleted = [NSMutableArray arrayWithCapacity:4];
97 [(NSMutableArray *)*_deleted addObject:obj];
101 /* scan new array for new objects */
103 for (i = 0, count = [_pub count]; i < count; i++) {
106 obj = [_pub objectAtIndex:i];
107 if (![obj isNotNull]) continue;
109 if ([_old containsObject:obj]) /* already processed */
112 if (_new == NULL) continue;
113 if (*_new == nil) *_new = [NSMutableArray arrayWithCapacity:16];
114 [(NSMutableArray *)*_new addObject:obj];
120 - (NSException *)publishVToDos:(NSArray *)_todos {
121 // TODO: work on tasks folder?
123 if ([_todos count] == 0)
127 return [NSException exceptionWithHTTPStatus:501 /* Not Implemented */
128 reason:@"server does not support vtodo PUTs!"];
130 return nil /* means: OK */;
134 - (NSException *)writeNewVEvents:(NSArray *)_events {
135 SOGoAppointmentFolder *folder;
139 if ((folder = [self clientObject]) == nil) {
140 return [NSException exceptionWithHTTPStatus:404 /* Not Found */
141 reason:@"did not find clientObject?!"];
144 for (i = 0, count = [_events count]; i < count; i++) {
145 SOGoAppointmentObject *object;
149 event = [_events objectAtIndex:i];
150 ical = [event versitString];
152 if (![ical isNotNull] && ([ical length] == 0)) {
153 [self logWithFormat:@"ERROR: got no ical representation of event: %@",
158 object = [folder lookupName:[event uid] inContext:[self context]
160 if (![object isNotNull]) {
162 [self logWithFormat:@"ERROR: could not lookup event: %@", [event uid]];
166 if ((error = [object saveContentString:ical]) != nil) /* failed, abort */
169 return nil; // TODO: fake OK
172 - (NSException *)updateVEvents:(NSArray *)_events {
173 if ([_events count] == 0)
176 [self logWithFormat:@"TODO: should update: %@", _events];
177 return nil; // TODO: fake OK
180 - (NSException *)deleteUIDs:(NSArray *)_uids {
181 if ([_uids count] == 0)
184 [self logWithFormat:@"TODO: should delete UIDs: %@",
185 [_uids componentsJoinedByString:@", "]];
186 return nil; // TODO: fake OK
189 - (NSException *)publishVEvents:(NSArray *)_events {
190 // TODO: extract UIDs and compare sets
193 NSArray *new, *updated, *deleted;
195 [self debugWithFormat:@"publish %d events ...", [_events count]];
199 availUIDs = [[self clientObject] toOneRelationshipKeys];
201 /* build changeset */
203 [self extractNewVEvents:&new updatedVEvents:&updated andDeletedUIDs:&deleted
204 whenComparingOldUIDs:availUIDs
205 withNewVEvents:_events];
209 if ([new count] > 0) {
210 if ((ex = [self writeNewVEvents:new]) != nil)
214 if ([updated count] > 0) {
215 if ((ex = [self updateVEvents:updated]) != nil)
219 if ([deleted count] > 0) {
220 if ((ex = [self deleteUIDs:deleted]) != nil)
224 return nil /* means: OK */;
227 - (NSException *)publishICalCalendar:(iCalCalendar *)_iCal {
230 [self debugWithFormat:@"publish iCalCalendar: %@", _iCal];
232 if ((ex = [self publishVEvents:[_iCal events]]) != nil)
235 if ((ex = [self publishVToDos:[_iCal todos]]) != nil)
238 return nil /* means: OK */;
241 - (NSException *)publishICalendarString:(NSString *)_ical {
245 [parser parseFromSource:_ical];
246 root = [[sax rootObject] retain]; /* retain to keep it around */
249 if (![root isNotNull]) {
250 [self debugWithFormat:@"invalid iCal input: %@", _ical];
251 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
252 reason:@"could not parse iCal input?!"];
255 if ([root isKindOfClass:[NSException class]])
256 return [root autorelease];
258 if ([root isKindOfClass:[iCalCalendar class]]) {
259 ex = [self publishICalCalendar:root];
262 ex = [NSException exceptionWithHTTPStatus:501 /* Not Implemented */
263 reason:@"cannot deal with input"];
266 [root release]; root = nil;
267 return ex /* means: OK */;
272 - (WOResponse *)publishOkResponse {
275 r = [[self context] response];
276 [r setStatus:200 /* OK */];
282 - (id)defaultAction {
284 Note: Apple iCal.app submits no content-type!
288 s = [[[self context] request] contentAsString];
289 if ([s length] == 0) {
290 [self debugWithFormat:@"missing content!"];
291 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
292 reason:@"missing iCalendar content"];
295 if ([s hasPrefix:@"BEGIN:VCALENDAR"]) {
298 if ((e = [self publishICalendarString:s]) == nil)
299 return [self publishOkResponse];
304 [self debugWithFormat:@"ERROR: cannot process input: %@", s];
306 /* Fake successful publish ... */
307 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
308 reason:@"invalid input format"];
313 - (BOOL)isDebuggingEnabled {
317 @end /* SOGoICalFilePublish */