/* 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. */ // WORK IN PROGRESS #include /* CardDAV multiget REPORT Sample request: /zidestore/dav/adam/Contacts/10931.vcf /zidestore/dav/adam/Contacts/10941.vcf /zidestore/dav/adam/Contacts/10951.vcf Sample response: zidestore/dav/adam/Contacts/10931.vcf 10931 BEGIN:VCARD ... END:VCARD HTTP/1.1 200 OK */ @class NSString, NSDate, NSArray, NSMutableArray, NSEnumerator; @class WORequest, WOResponse, WOContext; @interface SxDavAddrbookMultiget : 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 *)fetchContactsInContext:(id)_ctx; @end #include "SxAddressFolder.h" #include #include #include "common.h" @implementation SxDavAddrbookMultiget 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:@"CardDAV Multiget on: %@", [self clientObject]]; [self debugWithFormat:@" ids: %@", self->ids]; } // retrieve the contacts for the requested ids self->results = [[self fetchContactsInContext:[self context]] retain]; // if there are no valid requested contacts 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; } // build the response [self appendToResponse:[[self context] response] inContext:[self context]]; return [[self context] response]; } /* fetching */ - (SxContactManager *)contactManagerInContext:(id)_ctx { return [[self clientObject] contactManagerInContext:_ctx]; } - (NSEnumerator *)fetchContactsInContext:(id)_ctx { SxContactManager *manager; NSArray *contactIDs; LSCommandContext *ctx; if (_ctx == nil) return nil; manager = [self contactManagerInContext:_ctx]; // get an OGo commandContext so we can have a typeManager ctx = [[self clientObject] commandContextInContext:[self context]]; contactIDs = [[ctx typeManager] globalIDsForPrimaryKeys:self->ids]; if ([contactIDs isNotEmpty]) { if ([self isDebuggingEnabled]) [self debugWithFormat:@"got %d contacts ...", [contactIDs count]]; return [manager idsAndVersionsAndVCardsForGlobalIDs:contactIDs]; } [self debugWithFormat:@"got no contacts for request"]; return nil; } /* generate response */ - (NSString *)hrefForContact:(id)_contact inContext:(WOContext *)_ctx { NSString *u; id pkey; if (![(pkey = [_contact valueForKey:@"companyId"]) isNotEmpty]) return nil; u = [[self clientObject] baseURLInContext:_ctx]; return [u stringByAppendingFormat: ([u hasSuffix:@"/"] ? @"%@.vcf" : @"/%@.vcf"), pkey]; } - (void)appendToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx { id contact; [_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 ((contact = [self->results nextObject]) != nil) { NSString *href; id vCard; if ([self isDebuggingEnabled]) [self debugWithFormat:@"CONTACT: %@", contact]; href = [self hrefForContact:contact inContext:_ctx]; vCard = [contact valueForKey:@"vCardData"]; [_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:[[contact valueForKey:@"companyId"] stringValue]]; [_r appendContentString:@":"]; [_r appendContentString:[[contact valueForKey:@"objectVersion"] stringValue]]; [_r appendContentString:@"\n"]; /* content-type */ // TBD (if requested?) /* vCard */ if ([vCard isNotEmpty]) { [_r appendContentString:@""]; [_r appendContentXMLString:[vCard stringValue]]; [_r appendContentString:@"\n"]; } else { [self errorWithFormat:@"got no vCard data for contact: %@", contact]; } [_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:@".vcf"]) { 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 .vcf suffix"]; } } /* debugging */ - (BOOL)isDebuggingEnabled { return debugOn; } @end /* SxDavAddrbookMultiget */