/* Copyright (C) 2006-2009 Helge Hess Copyright (C) 2006-2009 Adam Williams 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 /* CalDAV calendar-query REPORT Sample request: /zidestore/dav/adam/Overview/10931.ics Sample response: zidestore/dav/adam/Overview/10931.ics 10931 BEGIN:VCALENDAR ... END:VCALENDAR HTTP/1.1 200 OK */ @class NSString, NSDate, NSArray, NSMutableArray, NSEnumerator; @class WORequest, WOResponse, WOContext; @interface SxDavCalendarMultiget : WODirectAction { /* nil - not requested, empty - all requested */ NSMutableArray *ids; NSEnumerator *results; } - (void)takeValuesFromRequest:(WORequest *)_rq inContext:(WOContext *)_ctx; - (void)appendToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx; - (void)takeKeyFromHref:(NSString *)_href; - (NSEnumerator *)fetchDatesInContext:(id)_ctx; @end #include "SxAppointmentFolder.h" #include #include #include #include "common.h" @implementation SxDavCalendarMultiget static BOOL debugOn = NO; - (void)dealloc { [self->results release]; [self->ids release]; [super dealloc]; } /* action */ - (id)defaultAction { // collect the requested ids from the request [self takeValuesFromRequest:[self request] inContext:[self context]]; if ([self isDebuggingEnabled]) { [self debugWithFormat:@"CalDAV Multiget on: %@", [self clientObject]]; [self debugWithFormat:@" ids: %@", self->ids]; } // retrieve the dates for the requested ids self->results = [[self fetchDatesInContext:[self context]] retain]; // if there are no valid requested dates make an empty enumerator if (self->results == nil) { self->results = [[[NSArray array] objectEnumerator] retain]; } else if ([self->results isKindOfClass:[NSException class]]) { [self errorWithFormat:@"failed to fetch: %@", self->results]; return self->results; } // buld the response [self appendToResponse:[[self context] response] inContext:[self context]]; return [[self context] response]; } /* fetching */ - (SxAptManager *)aptManagerInContext:(id)_ctx { return [[self clientObject] aptManagerInContext:_ctx]; } - (NSEnumerator *)fetchDatesInContext:(id)_ctx { SxAptManager *manager; NSMutableArray *dates; LSCommandContext *ctx; id tm; id tmp; NSEnumerator *enumerator; if (_ctx != nil) { manager = [self aptManagerInContext:_ctx]; // get an OGo commandContext so we can have a typeManager ctx = [[self clientObject] commandContextInContext:[self context]]; tm = [ctx typeManager]; dates = [NSMutableArray arrayWithCapacity:[self->ids count]]; enumerator = [self->ids objectEnumerator]; // make an array of gids for each key (id) that represents // a valid date. If you pass a non-date gid to the // SxAptManager it will die with a signal 6. // TBD(hh): why, how? backtrace? while ((tmp = [enumerator nextObject]) != nil) { tmp = [tm globalIDForPrimaryKey:tmp]; if (tmp != nil) { if ([[tmp entityName] isEqualToString:@"Date"]) { [dates addObject:tmp]; } } } if ([dates isNotEmpty]) { if ([self isDebuggingEnabled]) [self debugWithFormat:@"got %d events ...", [dates count]]; return [manager pkeysAndModDatesAndICalsForGlobalIDs:dates]; } else [self logWithFormat:@"got no dates for request"]; } return nil; } /* generate response */ - (NSString *)hrefForEvent:(id)_event inContext:(WOContext *)_ctx { NSString *u; id pkey; if (![(pkey = [_event valueForKey:@"pkey"]) isNotEmpty]) return nil; u = [[self clientObject] baseURLInContext:_ctx]; return [u stringByAppendingFormat: ([u hasSuffix:@"/"] ? @"%@.ics" : @"/%@.ics"), pkey]; } - (void)appendToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx { id event; [_r setContentEncoding:NSUTF8StringEncoding]; [_r setStatus:207 /* multistatus */]; [_r setHeader:@"text/xml; charset=\"utf-8\"" forKey:@"content-type"]; /* open multistatus */ [_r appendContentString:@"\n"]; [_r appendContentString:@"\n"]; /* generate events */ while ((event = [self->results nextObject]) != nil) { id ical, href; href = [self hrefForEvent:event inContext:_ctx]; ical = [event valueForKey:@"iCalData"]; [_r appendContentString:@" \n"]; [_r appendContentString:@" "]; [_r appendContentXMLString:href]; [_r appendContentString:@"\n"]; /* successful properties */ [_r appendContentString:@" \n"]; [_r appendContentString:@" HTTP/1.1 200 OK\n"]; [_r appendContentString:@" \n"]; /* etag */ [_r appendContentString:@""]; // int's can't contain XML special chars ... (no appendContentXMLString) // TBD: might need quotes, not sure (eg "233:23") [_r appendContentString:[[event valueForKey:@"pkey"] stringValue]]; [_r appendContentString:@":"]; [_r appendContentString:[[event valueForKey:@"version"] stringValue]]; [_r appendContentString:@"\n"]; /* ical */ if ([ical isNotEmpty]) { [_r appendContentString:@""]; [_r appendContentString:@"BEGIN:VCALENDAR\r\n"]; [_r appendContentString:@"VERSION:2.0\r\n"]; [_r appendContentXMLString:[ical stringValue]]; [_r appendContentString:@"END:VCALENDAR\r\n"]; [_r appendContentString:@"\n"]; } else { [self errorWithFormat:@"got no iCalendar data for event: %@", event]; } [_r appendContentString:@" \n"]; [_r appendContentString:@" \n"]; [_r appendContentString:@" \n"]; } /* end while */ /* close multistatus */ [_r appendContentString:@""]; } /* decoding requests */ - (void)takeValuesFromRequest:(WORequest *)_rq inContext:(WOContext *)_ctx { id queryElement; id children; unsigned i, count; self->ids = [[NSMutableArray alloc] initWithCapacity:128]; queryElement = [[_rq contentAsDOMDocument] documentElement]; children = [queryElement childNodes]; for (i = 0, count = [children length]; i < count; i++) { id node; node = [children objectAtIndex:i]; if ([node nodeType] != DOM_ELEMENT_NODE) continue; if ([[node localName] isEqualToString:@"href"]) { id href; href = [[node childNodes] objectAtIndex:0]; if (href != nil) [self takeKeyFromHref:[href nodeValue]]; } } } - (void)takeKeyFromHref:(NSString *)_href { NSString *key; if ([_href hasSuffix:@".ics"]) { key = [[_href pathComponents] lastObject]; key = [[key componentsSeparatedByString:@"."] objectAtIndex:0]; if ([key intValue] > 0) [self->ids addObject:intObj([key intValue])]; } else { [self warnWithFormat:@"href in multiget lacks .ics suffix"]; } } /* debugging */ - (BOOL)isDebuggingEnabled { return debugOn; } @end /* SxDavCalendarMultiget */