/* Copyright (C) 2006-2009 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG 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. */ #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import "SOGoContactGCSEntry.h" #import "SOGoContactGCSList.h" #import "SOGoContactGCSFolder.h" #define folderListingFields [NSArray arrayWithObjects: @"c_name", @"c_cn", \ @"c_givenname", @"c_sn", @"c_screenname", \ @"c_o", @"c_mail", @"c_telephonenumber", \ nil] @implementation SOGoContactGCSFolder - (Class) objectClassForContent: (NSString *) content { CardGroup *cardEntry; NSString *firstTag; Class objectClass; objectClass = Nil; cardEntry = [CardGroup parseSingleFromSource: content]; if (cardEntry) { firstTag = [[cardEntry tag] uppercaseString]; if ([firstTag isEqualToString: @"VCARD"]) objectClass = [SOGoContactGCSEntry class]; else if ([firstTag isEqualToString: @"VLIST"]) objectClass = [SOGoContactGCSList class]; } return objectClass; } - (Class) objectClassForComponentName: (NSString *) componentName { Class objectClass; if ([componentName isEqualToString: @"vcard"]) objectClass = [SOGoContactGCSEntry class]; else if ([componentName isEqualToString: @"vlist"]) objectClass = [SOGoContactGCSList class]; else objectClass = Nil; return objectClass; } - (Class) objectClassForResourceNamed: (NSString *) name { EOQualifier *qualifier; NSArray *records; NSString *component; Class objectClass; qualifier = [EOQualifier qualifierWithQualifierFormat:@"c_name = %@", name]; records = [[self ocsFolder] fetchFields: [NSArray arrayWithObject: @"c_component"] matchingQualifier: qualifier]; if ([records count]) { component = [[records objectAtIndex: 0] valueForKey: @"c_component"]; objectClass = [self objectClassForComponentName: component]; } else objectClass = Nil; return objectClass; } - (BOOL) requestNamedIsHandledLater: (NSString *) name { return [name isEqualToString: @"OPTIONS"]; } - (id) lookupName: (NSString *)_key inContext: (id)_ctx acquire: (BOOL)_flag { id obj; NSString *url; BOOL handledLater; /* first check attributes directly bound to the application */ handledLater = [self requestNamedIsHandledLater: _key]; if (handledLater) obj = nil; else { obj = [super lookupName:_key inContext:_ctx acquire:NO]; if (!obj) { if ([self isValidContentName: _key]) { url = [[[_ctx request] uri] urlWithoutParameters]; if ([url hasSuffix: @"AsContact"]) obj = [SOGoContactGCSEntry objectWithName: _key inContainer: self]; else if ([url hasSuffix: @"AsList"]) obj = [SOGoContactGCSList objectWithName: _key inContainer: self]; [obj setIsNew: YES]; } } if (!obj) obj = [NSException exceptionWithHTTPStatus:404 /* Not Found */]; } if (obj) [[SOGoCache sharedCache] registerObject: obj withName: _key inContainer: container]; return obj; } - (EOQualifier *) _qualifierForFilter: (NSString *) filter { NSString *qs; EOQualifier *qualifier; if (filter && [filter length] > 0) { filter = [[filter stringByReplacingString: @"\\" withString: @"\\\\"] stringByReplacingString: @"'" withString: @"\\'\\'"]; qs = [NSString stringWithFormat: @"(c_sn isCaseInsensitiveLike: '%@%%') OR " @"(c_givenname isCaseInsensitiveLike: '%@%%') OR " @"(c_cn isCaseInsensitiveLike: '%@%%') OR " @"(c_mail isCaseInsensitiveLike: '%@%%') OR " @"(c_telephonenumber isCaseInsensitiveLike: '%%%@%%') OR " @"(c_o isCaseInsensitiveLike: '%%%@%%')", filter, filter, filter, filter, filter, filter]; qualifier = [EOQualifier qualifierWithQualifierFormat: qs]; } else qualifier = nil; return qualifier; } - (void) fixupContactRecord: (NSMutableDictionary *) contactRecord { NSString *data; data = [contactRecord objectForKey: @"c_cn"]; if (![data length]) { data = [contactRecord keysWithFormat: @"%{c_givenname} %{c_sn}"]; [contactRecord setObject: data forKey: @"c_cn"]; } if (![contactRecord objectForKey: @"c_mail"]) [contactRecord setObject: @"" forKey: @"c_mail"]; if (![contactRecord objectForKey: @"c_screenname"]) [contactRecord setObject: @"" forKey: @"c_screenname"]; if (![contactRecord objectForKey: @"c_o"]) [contactRecord setObject: @"" forKey: @"c_o"]; if (![contactRecord objectForKey: @"c_telephonenumber"]) [contactRecord setObject: @"" forKey: @"c_telephonenumber"]; } - (NSArray *) _flattenedRecords: (NSArray *) records { NSMutableArray *newRecords; NSEnumerator *oldRecords; NSDictionary *oldRecord; NSMutableDictionary *newRecord; newRecords = [NSMutableArray arrayWithCapacity: [records count]]; oldRecords = [records objectEnumerator]; oldRecord = [oldRecords nextObject]; while (oldRecord) { newRecord = [NSMutableDictionary dictionaryWithDictionary: oldRecord]; [self fixupContactRecord: newRecord]; [newRecords addObject: newRecord]; oldRecord = [oldRecords nextObject]; } return newRecords; } - (NSDictionary *) lookupContactWithName: (NSString *) aName { NSArray *fields, *dbRecords, *records; NSDictionary *record; EOQualifier *qualifier; NSString *qs; fields = folderListingFields; record = nil; if (aName && [aName length] > 0) { aName = [[aName stringByReplacingString: @"\\" withString: @"\\\\"] stringByReplacingString: @"'" withString: @"\\'\\'"]; qs = [NSString stringWithFormat: @"(c_name isCaseInsensitiveLike: '%@')", aName]; qualifier = [EOQualifier qualifierWithQualifierFormat: qs]; dbRecords = [[self ocsFolder] fetchFields: fields matchingQualifier: qualifier]; if ([dbRecords count] > 0) { records = [self _flattenedRecords: dbRecords]; record = [records lastObject]; } } return record; } - (NSArray *) lookupContactsWithFilter: (NSString *) filter sortBy: (NSString *) sortKey ordering: (NSComparisonResult) sortOrdering { NSArray *fields, *dbRecords, *records; EOQualifier *qualifier; EOSortOrdering *ordering; fields = folderListingFields; qualifier = [self _qualifierForFilter: filter]; dbRecords = [[self ocsFolder] fetchFields: fields matchingQualifier: qualifier]; if ([dbRecords count] > 0) { records = [self _flattenedRecords: dbRecords]; ordering = [EOSortOrdering sortOrderingWithKey: sortKey selector: ((sortOrdering == NSOrderedDescending) ? EOCompareCaseInsensitiveDescending : EOCompareCaseInsensitiveAscending)]; records = [records sortedArrayUsingKeyOrderArray: [NSArray arrayWithObject: ordering]]; } else records = nil; [self debugWithFormat:@"fetched %i records.", [records count]]; return records; } #warning this should be unified within SOGoFolder - (void) appendObject: (NSDictionary *) object withBaseURL: (NSString *) baseURL toREPORTResponse: (WOResponse *) r { SOGoContactGCSEntry *component; NSString *name, *etagLine, *contactString; name = [object objectForKey: @"c_name"]; component = [self lookupName: name inContext: context acquire: NO]; [r appendContentString: @" \r\n"]; [r appendContentString: @" "]; [r appendContentString: baseURL]; if (![baseURL hasSuffix: @"/"]) [r appendContentString: @"/"]; [r appendContentString: name]; [r appendContentString: @"\r\n"]; [r appendContentString: @" \r\n"]; [r appendContentString: @" \r\n"]; etagLine = [NSString stringWithFormat: @" %@\r\n", [component davEntityTag]]; [r appendContentString: etagLine]; [r appendContentString: @" \r\n"]; [r appendContentString: @" HTTP/1.1 200 OK\r\n"]; [r appendContentString: @" \r\n"]; [r appendContentString: @" "]; contactString = [[component contentAsString] stringByEscapingXMLString]; [r appendContentString: contactString]; [r appendContentString: @"\r\n"]; [r appendContentString: @" \r\n"]; } - (NSArray *) davComplianceClassesInContext: (id)_ctx { NSMutableArray *classes; NSArray *primaryClasses; classes = [NSMutableArray new]; [classes autorelease]; primaryClasses = [super davComplianceClassesInContext: _ctx]; if (primaryClasses) [classes addObjectsFromArray: primaryClasses]; [classes addObject: @"access-control"]; [classes addObject: @"addressbook-access"]; return classes; } - (NSString *) groupDavResourceType { return @"vcard-collection"; } - (NSArray *) davResourceType { NSMutableArray *resourceType; NSArray *cardDavCollection; cardDavCollection = [NSArray arrayWithObjects: @"addressbook", @"urn:ietf:params:xml:ns:carddav", nil]; resourceType = [NSMutableArray arrayWithArray: [super davResourceType]]; [resourceType addObject: cardDavCollection]; return resourceType; } /* sorting */ - (NSComparisonResult) compare: (id) otherFolder { NSComparisonResult comparison; if ([NSStringFromClass([otherFolder class]) isEqualToString: @"SOGoContactSourceFolder"]) comparison = NSOrderedAscending; else comparison = [super compare: otherFolder]; return comparison; } /* folder type */ - (NSString *) folderType { return @"Contact"; } - (NSString *) outlookFolderClass { return @"IPF.Contact"; } @end /* SOGoContactGCSFolder */