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 <SOGo/SOGoAppointment.h>
34 #include <NGiCal/NGiCal.h>
35 #include <NGiCal/iCalRenderer.h>
36 #include <SaxObjC/SaxObjC.h>
39 @implementation SOGoICalFilePublish
41 static BOOL debugOn = YES;
42 static id<NSObject,SaxXMLReader> parser = nil;
43 static SaxObjectDecoder *sax = nil;
47 parser = [[[SaxXMLReaderFactory standardXMLReaderFactory]
48 createXMLReaderForMimeType:@"text/calendar"]
51 NSLog(@"ERROR: did not find a parser for text/calendar!");
54 sax = [[SaxObjectDecoder alloc] initWithMappingNamed:@"NGiCal"];
56 NSLog(@"ERROR: could not create the iCal SAX handler!");
59 [parser setContentHandler:sax];
60 [parser setErrorHandler:sax];
66 return [[super clientObject] aptFolderInContext:[self context]];
71 - (void)extractNewVEvents:(NSArray **)_new updatedVEvents:(NSArray **)_updated
72 andDeletedUIDs:(NSArray **)_deleted
73 whenComparingOldUIDs:(NSArray *)_old withNewVEvents:(NSArray *)_pub
77 if (_new != NULL ) *_new = nil;
78 if (_updated != NULL ) *_updated = nil;
79 if (_deleted != NULL ) *_deleted = nil;
81 /* scan old array for changes */
83 for (i = 0, count = [_old count]; i < count; i++) {
86 obj = [_old objectAtIndex:i];
87 if (![obj isNotNull]) continue;
89 if ([_pub containsObject:obj]) {
90 /* updated object, in both sets */
91 if (_updated == NULL) continue;
92 if (*_updated == nil) *_updated = [NSMutableArray arrayWithCapacity:16];
93 [(NSMutableArray *)*_updated addObject:obj];
96 /* deleted object, only in old set */
97 if (_deleted == NULL) continue;
98 if (*_deleted == nil) *_deleted = [NSMutableArray arrayWithCapacity:4];
99 [(NSMutableArray *)*_deleted addObject:obj];
103 /* scan new array for new objects */
105 for (i = 0, count = [_pub count]; i < count; i++) {
108 obj = [_pub objectAtIndex:i];
109 if (![obj isNotNull]) continue;
111 if ([_old containsObject:obj]) /* already processed */
114 if (_new == NULL) continue;
115 if (*_new == nil) *_new = [NSMutableArray arrayWithCapacity:16];
116 [(NSMutableArray *)*_new addObject:obj];
122 - (NSException *)publishVToDos:(NSArray *)_todos {
123 // TODO: work on tasks folder?
125 if ([_todos count] == 0)
129 return [NSException exceptionWithHTTPStatus:501 /* Not Implemented */
130 reason:@"server does not support vtodo PUTs!"];
132 return nil /* means: OK */;
136 - (NSException *)writeNewVEvents:(NSArray *)_events {
137 SOGoAppointmentFolder *folder;
138 iCalRenderer *renderer;
142 if ((folder = [self clientObject]) == nil) {
143 return [NSException exceptionWithHTTPStatus:404 /* Not Found */
144 reason:@"did not find clientObject?!"];
147 renderer = [iCalRenderer sharedICalendarRenderer];
149 for (i = 0, count = [_events count]; i < count; i++) {
150 SOGoAppointmentObject *object;
154 event = [_events objectAtIndex:i];
155 ical = [renderer iCalendarStringForEvent:event];
157 if (![ical isNotNull] && ([ical length] == 0)) {
158 [self logWithFormat:@"ERROR: got no ical representation of event: %@",
163 object = [folder lookupName:[event uid] inContext:[self context]
165 if (![object isNotNull]) {
167 [self logWithFormat:@"ERROR: could not lookup event: %@", [event uid]];
171 if ((error = [object saveContentString:ical]) != nil) /* failed, abort */
174 return nil; // TODO: fake OK
177 - (NSException *)updateVEvents:(NSArray *)_events {
178 if ([_events count] == 0)
181 [self logWithFormat:@"TODO: should update: %@", _events];
182 return nil; // TODO: fake OK
185 - (NSException *)deleteUIDs:(NSArray *)_uids {
186 if ([_uids count] == 0)
189 [self logWithFormat:@"TODO: should delete UIDs: %@",
190 [_uids componentsJoinedByString:@", "]];
191 return nil; // TODO: fake OK
194 - (NSException *)publishVEvents:(NSArray *)_events {
195 // TODO: extract UIDs and compare sets
198 NSArray *new, *updated, *deleted;
200 [self debugWithFormat:@"publish %d events ...", [_events count]];
204 availUIDs = [[self clientObject] toOneRelationshipKeys];
206 /* build changeset */
208 [self extractNewVEvents:&new updatedVEvents:&updated andDeletedUIDs:&deleted
209 whenComparingOldUIDs:availUIDs
210 withNewVEvents:_events];
214 if ([new count] > 0) {
215 if ((ex = [self writeNewVEvents:new]) != nil)
219 if ([updated count] > 0) {
220 if ((ex = [self updateVEvents:updated]) != nil)
224 if ([deleted count] > 0) {
225 if ((ex = [self deleteUIDs:deleted]) != nil)
229 return nil /* means: OK */;
232 - (NSException *)publishICalCalendar:(iCalCalendar *)_iCal {
235 [self debugWithFormat:@"publish iCalCalendar: %@", _iCal];
237 if ((ex = [self publishVEvents:[_iCal events]]) != nil)
240 if ((ex = [self publishVToDos:[_iCal todos]]) != nil)
243 return nil /* means: OK */;
246 - (NSException *)publishICalendarString:(NSString *)_ical {
250 [parser parseFromSource:_ical];
251 root = [[sax rootObject] retain]; /* retain to keep it around */
254 if (![root isNotNull]) {
255 [self debugWithFormat:@"invalid iCal input: %@", _ical];
256 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
257 reason:@"could not parse iCal input?!"];
260 if ([root isKindOfClass:[NSException class]])
261 return [root autorelease];
263 if ([root isKindOfClass:[iCalCalendar class]]) {
264 ex = [self publishICalCalendar:root];
267 ex = [NSException exceptionWithHTTPStatus:501 /* Not Implemented */
268 reason:@"cannot deal with input"];
271 [root release]; root = nil;
272 return ex /* means: OK */;
277 - (WOResponse *)publishOkResponse {
280 r = [[self context] response];
281 [r setStatus:200 /* OK */];
287 - (id)defaultAction {
289 Note: Apple iCal.app submits no content-type!
293 s = [[[self context] request] contentAsString];
294 if ([s length] == 0) {
295 [self debugWithFormat:@"missing content!"];
296 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
297 reason:@"missing iCalendar content"];
300 if ([s hasPrefix:@"BEGIN:VCALENDAR"]) {
303 if ((e = [self publishICalendarString:s]) == nil)
304 return [self publishOkResponse];
309 [self debugWithFormat:@"ERROR: cannot process input: %@", s];
311 /* Fake successful publish ... */
312 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
313 reason:@"invalid input format"];
318 - (BOOL)isDebuggingEnabled {
322 @end /* SOGoICalFilePublish */