]> err.no Git - scalable-opengroupware.org/blob - Protocols/iCalHTTP/SOGoICalFilePublish.m
git-svn-id: http://svn.opengroupware.org/SOGo/trunk@900 d1b88da0-ebda-0310-925b-ed51d...
[scalable-opengroupware.org] / Protocols / iCalHTTP / SOGoICalFilePublish.m
1 /*
2   Copyright (C) 2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include <NGObjWeb/WODirectAction.h>
23
24 @interface SOGoICalFilePublish : WODirectAction
25 {
26 }
27
28 @end
29
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>
37 #include "common.h"
38
39 @implementation SOGoICalFilePublish
40
41 static BOOL debugOn = YES;
42 static id<NSObject,SaxXMLReader> parser = nil;
43 static SaxObjectDecoder          *sax   = nil;
44
45 + (void)initialize {
46   if (parser == nil) {
47     parser = [[[SaxXMLReaderFactory standardXMLReaderFactory] 
48                 createXMLReaderForMimeType:@"text/calendar"]
49                 retain];
50     if (parser == nil)
51       NSLog(@"ERROR: did not find a parser for text/calendar!");
52   }
53   if (sax == nil) {
54     sax = [[SaxObjectDecoder alloc] initWithMappingNamed:@"NGiCal"];
55     if (sax == nil)
56       NSLog(@"ERROR: could not create the iCal SAX handler!");
57   }
58   
59   [parser setContentHandler:sax];
60   [parser setErrorHandler:sax];
61 }
62
63 /* clientObject */
64
65 - (id)clientObject {
66   return [[super clientObject] aptFolderInContext:[self context]];
67 }
68
69 /* change sets */
70
71 - (void)extractNewVEvents:(NSArray **)_new updatedVEvents:(NSArray **)_updated
72   andDeletedUIDs:(NSArray **)_deleted
73   whenComparingOldUIDs:(NSArray *)_old withNewVEvents:(NSArray *)_pub
74 {
75   unsigned i, count;
76   
77   if (_new     != NULL ) *_new     = nil;
78   if (_updated != NULL ) *_updated = nil;
79   if (_deleted != NULL ) *_deleted = nil;
80   
81   /* scan old array for changes */
82   
83   for (i = 0, count = [_old count]; i < count; i++) {
84     id obj;
85     
86     obj = [_old objectAtIndex:i];
87     if (![obj isNotNull]) continue;
88     
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];
94     }
95     else {
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];
100     }
101   }
102
103   /* scan new array for new objects */
104
105   for (i = 0, count = [_pub count]; i < count; i++) {
106     id obj;
107     
108     obj = [_pub objectAtIndex:i];
109     if (![obj isNotNull]) continue;
110     
111     if ([_old containsObject:obj]) /* already processed */
112       continue;
113     
114     if (_new == NULL) continue;
115     if (*_new == nil) *_new = [NSMutableArray arrayWithCapacity:16];
116     [(NSMutableArray *)*_new addObject:obj];
117   }
118 }
119
120 /* operation */
121
122 - (NSException *)publishVToDos:(NSArray *)_todos {
123   // TODO: work on tasks folder?
124   
125   if ([_todos count] == 0)
126     return nil;
127   
128 #if 1
129   return [NSException exceptionWithHTTPStatus:501 /* Not Implemented */
130                       reason:@"server does not support vtodo PUTs!"];
131 #else
132   return nil /* means: OK */;
133 #endif
134 }
135
136 - (NSException *)writeNewVEvents:(NSArray *)_events {
137   SOGoAppointmentFolder *folder;
138   iCalRenderer *renderer;
139   NSException *error;
140   unsigned i, count;
141   
142   if ((folder = [self clientObject]) == nil) {
143     return [NSException exceptionWithHTTPStatus:404 /* Not Found */
144                         reason:@"did not find clientObject?!"];
145   }
146
147   renderer = [iCalRenderer sharedICalendarRenderer];
148   
149   for (i = 0, count = [_events count]; i < count; i++) {
150     SOGoAppointmentObject *object;
151     iCalEvent *event;
152     NSString  *ical;
153     
154     event = [_events objectAtIndex:i];
155     ical  = [renderer iCalendarStringForEvent:event];
156
157     if (![ical isNotNull] && ([ical length] == 0)) {
158       [self logWithFormat:@"ERROR: got no ical representation of event: %@",
159               event];
160       continue;
161     }
162     
163     object = [folder lookupName:[event uid] inContext:[self context]
164                      acquire:NO];
165     if (![object isNotNull]) {
166       // TODO: what to do?
167       [self logWithFormat:@"ERROR: could not lookup event: %@", [event uid]];
168       continue;
169     }
170     
171     if ((error = [object saveContentString:ical]) != nil) /* failed, abort */
172       return error;
173   }
174   return nil; // TODO: fake OK
175 }
176
177 - (NSException *)updateVEvents:(NSArray *)_events {
178   if ([_events count] == 0)
179     return nil;
180   
181   [self logWithFormat:@"TODO: should update: %@", _events];
182   return nil; // TODO: fake OK
183 }
184
185 - (NSException *)deleteUIDs:(NSArray *)_uids {
186   if ([_uids count] == 0)
187     return nil;
188   
189   [self logWithFormat:@"TODO: should delete UIDs: %@", 
190         [_uids componentsJoinedByString:@", "]];
191   return nil; // TODO: fake OK
192 }
193
194 - (NSException *)publishVEvents:(NSArray *)_events {
195   // TODO: extract UIDs and compare sets
196   NSException *ex;
197   NSArray *availUIDs;
198   NSArray *new, *updated, *deleted;
199   
200   [self debugWithFormat:@"publish %d events ...", [_events count]];
201   
202   /* find changeset */
203   
204   availUIDs = [[self clientObject] toOneRelationshipKeys];
205
206   /* build changeset */
207   
208   [self extractNewVEvents:&new updatedVEvents:&updated andDeletedUIDs:&deleted
209         whenComparingOldUIDs:availUIDs
210         withNewVEvents:_events];
211   
212   /* process */
213   
214   if ([new count] > 0) {
215     if ((ex = [self writeNewVEvents:new]) != nil)
216       return ex;
217   }
218   
219   if ([updated count] > 0) {
220     if ((ex = [self updateVEvents:updated]) != nil)
221       return ex;
222   }
223   
224   if ([deleted count] > 0) {
225     if ((ex = [self deleteUIDs:deleted]) != nil)
226       return ex;
227   }
228   
229   return nil /* means: OK */;
230 }
231
232 - (NSException *)publishICalCalendar:(iCalCalendar *)_iCal {
233   NSException *ex;
234   
235   [self debugWithFormat:@"publish iCalCalendar: %@", _iCal];
236
237   if ((ex = [self publishVEvents:[_iCal events]]) != nil)
238     return ex;
239   
240   if ((ex = [self publishVToDos:[_iCal todos]]) != nil)
241     return ex;
242   
243   return nil /* means: OK */;
244 }
245
246 - (NSException *)publishICalendarString:(NSString *)_ical {
247   NSException *ex;
248   id root;
249   
250   [parser parseFromSource:_ical];
251   root = [[sax rootObject] retain]; /* retain to keep it around */
252   [sax reset];
253   
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?!"];
258   }
259   
260   if ([root isKindOfClass:[NSException class]])
261     return [root autorelease];
262   
263   if ([root isKindOfClass:[iCalCalendar class]]) {
264     ex = [self publishICalCalendar:root];
265   }
266   else {
267     ex = [NSException exceptionWithHTTPStatus:501 /* Not Implemented */
268                       reason:@"cannot deal with input"];
269   }
270   
271   [root release]; root = nil;
272   return ex /* means: OK */;
273 }
274
275 /* responses */
276
277 - (WOResponse *)publishOkResponse {
278   WOResponse *r;
279   
280   r = [[self context] response];
281   [r setStatus:200 /* OK */];
282   return r;
283 }
284
285 /* actions */
286
287 - (id)defaultAction {
288   /*
289     Note: Apple iCal.app submits no content-type!
290   */
291   NSString *s;
292   
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"];
298   }
299   
300   if ([s hasPrefix:@"BEGIN:VCALENDAR"]) {
301     NSException *e;
302     
303     if ((e = [self publishICalendarString:s]) == nil)
304       return [self publishOkResponse];
305     
306     return e;
307   }
308   
309   [self debugWithFormat:@"ERROR: cannot process input: %@", s];
310   
311   /* Fake successful publish ... */
312   return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
313                       reason:@"invalid input format"];
314 }
315
316 /* debugging */
317
318 - (BOOL)isDebuggingEnabled {
319   return debugOn;
320 }
321
322 @end /* SOGoICalFilePublish */