sogo/SoObjects/Contacts/SOGoContactFolders.m

555 lines
17 KiB
Objective-C

/* SOGoContactFolders.m - this file is part of SOGo
*
* Copyright (C) 2006-2017 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSSortDescriptor.h>
#import <NGCards/NGVCard.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <DOM/DOMElement.h>
#import <GDLContentStore/GCSChannelManager.h>
#import <GDLContentStore/GCSFolderManager.h>
#import <GDLContentStore/NSURL+GCS.h>
#import <GDLAccess/EOAdaptorChannel.h>
#import <SOGo/NSObject+DAV.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserManager.h>
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/WORequest+SOGo.h>
#import "SOGoContactGCSFolder.h"
#import "SOGoContactSourceFolder.h"
#import "SOGoContactFolder.h"
#import "SOGoContactFolders.h"
Class SOGoContactSourceFolderK;
#define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav"
@implementation SOGoContactFolders
+ (void) initialize
{
SOGoContactSourceFolderK = [SOGoContactSourceFolder class];
}
+ (NSString *) gcsFolderType
{
return @"Contact";
}
+ (Class) subFolderClass
{
return [SOGoContactGCSFolder class];
}
- (void) _fetchLDAPAddressBooks: (id <SOGoSource>) source
{
id <SOGoSource> abSource;
SOGoContactSourceFolder *folder;
NSArray *abSources;
NSUInteger count, max;
NSString *name;
abSources = [source addressBookSourcesForUser: owner];
max = [abSources count];
for (count = 0; count < max; count++)
{
abSource = [abSources objectAtIndex: count];
name = [abSource sourceID];
folder = [SOGoContactSourceFolder folderWithName: name
andDisplayName: [abSource displayName]
inContainer: self];
[folder setSource: abSource];
[folder setIsPersonalSource: YES];
[subFolders setObject: folder forKey: name];
}
}
- (NSException *) appendPersonalSources
{
SOGoUser *currentUser;
NSException *result;
id <SOGoSource> source;
currentUser = [context activeUser];
source = [currentUser authenticationSource];
if ([source hasUserAddressBooks])
{
result = nil;
/* We don't handle ACLs for user LDAP addressbooks yet, therefore only
the owner has access to his addressbooks. */
if (activeUserIsOwner
|| [[currentUser login] isEqualToString: owner])
{
[self _fetchLDAPAddressBooks: source];
if (![subFolders objectForKey: @"personal"])
{
result = [source addAddressBookSource: @"personal"
withDisplayName: [self defaultFolderName]
forUser: owner];
if (!result)
[self _fetchLDAPAddressBooks: source];
}
}
}
else
result = [super appendPersonalSources];
return result;
}
- (NSException *) appendCollectedSources
{
GCSChannelManager *cm;
EOAdaptorChannel *fc;
NSURL *folderLocation;
NSString *sql, *gcsFolderType;
NSException *error;
cm = [GCSChannelManager defaultChannelManager];
folderLocation = [[GCSFolderManager defaultFolderManager] folderInfoLocation];
fc = [cm acquireOpenChannelForURL: folderLocation];
if ([fc isOpen])
{
gcsFolderType = [[self class] gcsFolderType];
sql = [NSString stringWithFormat: (@"SELECT c_path4 FROM %@"
@" WHERE c_path2 = '%@'"
@" AND c_folder_type = '%@'"),
[folderLocation gcsTableName], owner, gcsFolderType];
error = [super fetchSpecialFolders: sql withChannel: fc andFolderType: SOGoCollectedFolder];
[cm releaseChannel: fc];
}
else
error = [NSException exceptionWithName: @"SOGoDBException"
reason: @"database connection could not be open"
userInfo: nil];
return error;
}
- (NSDictionary *) systemSources
{
NSMutableDictionary *systemSources;
SOGoUserManager *um;
SOGoSystemDefaults *sd;
NSEnumerator *sourceIDs, *domains;
NSString *currentSourceID, *srcDisplayName, *domain;
SOGoContactSourceFolder *currentFolder;
SOGoUser *currentUser;
systemSources = [NSMutableDictionary dictionary];
if (! ([[context request] isIPhoneAddressBookApp] &&
![[context request] isAndroid]))
{
currentUser = [context activeUser];
if (activeUserIsOwner
|| [[currentUser login] isEqualToString: owner])
{
domain = [currentUser domain];
um = [SOGoUserManager sharedUserManager];
sd = [SOGoSystemDefaults sharedSystemDefaults];
domains = [[sd visibleDomainsForDomain: domain] objectEnumerator];
while (domain)
{
sourceIDs = [[um addressBookSourceIDsInDomain: domain]
objectEnumerator];
while ((currentSourceID = [sourceIDs nextObject]))
{
srcDisplayName
= [um displayNameForSourceWithID: currentSourceID];
currentFolder = [SOGoContactSourceFolder
folderWithName: currentSourceID
andDisplayName: srcDisplayName
inContainer: self];
[currentFolder setSource: [um sourceWithID: currentSourceID]];
[systemSources setObject: currentFolder forKey: currentSourceID];
}
domain = [domains nextObject];
}
}
}
return systemSources;
}
- (NSException *) appendSystemSources
{
[subFolders addEntriesFromDictionary: [self systemSources]];
return nil;
}
- (NSException *) newFolderWithName: (NSString *) name
andNameInContainer: (NSString *) newNameInContainer
{
SOGoUser *currentUser;
NSException *result;
id <SOGoSource> source;
currentUser = [context activeUser];
source = [currentUser authenticationSource];
if ([source hasUserAddressBooks])
{
result = nil;
/* We don't handle ACLs for user LDAP addressbooks yet, therefore only
the owner has access to his addressbooks. */
if (activeUserIsOwner
|| [[currentUser login] isEqualToString: owner])
{
result = [source addAddressBookSource: newNameInContainer
withDisplayName: name
forUser: owner];
if (!result)
[self _fetchLDAPAddressBooks: source];
}
}
else
result = [super newFolderWithName: name
andNameInContainer: newNameInContainer];
return result;
}
- (NSException *) renameLDAPAddressBook: (NSString *) sourceID
withDisplayName: (NSString *) newDisplayName
{
NSException *result;
SOGoUser *currentUser;
id <SOGoSource> source;
currentUser = [context activeUser];
source = [currentUser authenticationSource];
/* We don't handle ACLs for user LDAP addressbooks yet, therefore only
the owner has access to his addressbooks. */
if (activeUserIsOwner
|| [[currentUser login] isEqualToString: owner])
result = [source renameAddressBookSource: sourceID
withDisplayName: newDisplayName
forUser: owner];
else
result = [NSException exceptionWithHTTPStatus: 403
reason: @"operation denied"];
return result;
}
- (NSException *) removeLDAPAddressBook: (NSString *) sourceID
{
NSException *result;
SOGoUser *currentUser;
id <SOGoSource> source;
if ([sourceID isEqualToString: @"personal"]){
result = [NSException exceptionWithHTTPStatus: 403 reason: [NSString stringWithFormat: (@"folder '%@' cannot be deleted"), sourceID]];
}
else
{
result = nil;
currentUser = [context activeUser];
source = [currentUser authenticationSource];
/* We don't handle ACLs for user LDAP addressbooks yet, therefore only
the owner has access to his addressbooks. */
if (activeUserIsOwner
|| [[currentUser login] isEqualToString: owner])
result = [source removeAddressBookSource: sourceID
forUser: owner];
else
result = [NSException exceptionWithHTTPStatus: 403
reason: @"operation denied"];
}
return result;
}
- (NSString *) defaultFolderName
{
return [self labelForKey: @"Personal Address Book"];
}
- (NSString *) collectedFolderName
{
return [self labelForKey: @"Collected Address Book"];
}
- (NSArray *) toManyRelationshipKeys
{
NSMutableArray *keys;
if ([[context request] isMacOSXAddressBookApp])
keys = [NSMutableArray arrayWithObject: @"personal"];
else
keys = (NSMutableArray *) [super toManyRelationshipKeys];
return keys;
}
- (id) lookupName: (NSString *) name
inContext: (WOContext *) lookupContext
acquire: (BOOL) acquire
{
id folder = nil;
SOGoUser *currentUser;
SOGoUserManager *um;
SOGoSystemDefaults *sd;
NSEnumerator *domains;
NSArray *sourceIDs;
NSString *domain, *srcDisplayName;
if ([[context request] isIPhoneAddressBookApp] &&
![[context request] isAndroid])
{
currentUser = [context activeUser];
if (activeUserIsOwner
|| [[currentUser login] isEqualToString: owner])
{
domain = [currentUser domain];
um = [SOGoUserManager sharedUserManager];
sd = [SOGoSystemDefaults sharedSystemDefaults];
domains = [[sd visibleDomainsForDomain: domain] objectEnumerator];
while (domain)
{
sourceIDs = [um addressBookSourceIDsInDomain: domain];
if ([sourceIDs containsObject: name])
{
srcDisplayName = [um displayNameForSourceWithID: name];
folder = [SOGoContactSourceFolder
folderWithName: name
andDisplayName: srcDisplayName
inContainer: self];
[folder setSource: [um sourceWithID: name]];
}
domain = [domains nextObject];
}
}
}
if (!folder)
folder = [super lookupName: name inContext: lookupContext
acquire: acquire];
return folder;
}
- (NSException *) setDavContactsCategories: (NSString *) newCategories
{
SOGoUser *ownerUser;
NSMutableArray *categories;
id <DOMElement> documentElement, catNode;
id <DOMDocument> document;
id <DOMNodeList> catNodes;
NSUInteger count, max;
SOGoUserDefaults *ud;
categories = [NSMutableArray array];
if ([newCategories length] > 0)
{
document = [[context request] contentAsDOMDocument];
documentElement = [document documentElement];
catNodes = [documentElement getElementsByTagName: @"category"];
max = [catNodes length];
for (count = 0; count < max; count++)
{
catNode = [catNodes objectAtIndex: count];
if ([catNode hasChildNodes])
[categories addObject: [(NGDOMNode *) catNode textValue]];
}
}
// NSLog (@"setting categories to : %@", categories);
ownerUser = [SOGoUser userWithLogin: owner];
ud = [ownerUser userDefaults];
[ud setContactsCategories: categories];
[ud synchronize];
return nil;
}
- (SOGoWebDAVValue *) davContactsCategories
{
NSMutableArray *davCategories;
NSArray *categories;
NSUInteger count, max;
SOGoUser *ownerUser;
NSDictionary *catElement;
ownerUser = [SOGoUser userWithLogin: owner];
categories = [[ownerUser userDefaults] contactsCategories];
max = [categories count];
davCategories = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
catElement = davElementWithContent (@"category",
XMLNS_INVERSEDAV,
[categories objectAtIndex: count]);
[davCategories addObject: catElement];
}
return [davElementWithContent (@"contacts-categories",
XMLNS_INVERSEDAV,
davCategories)
asWebDAVValue];
}
- (NSArray *) allContactsFromFilter: (NSString *) theFilter
excludeGroups: (BOOL) excludeGroups
excludeLists: (BOOL) excludeLists
{
NSArray *folders, *contacts, *descriptors, *sortedContacts;
NSMutableDictionary *contact, *uniqueContacts;
NSMutableArray *sortedFolders;
SOGoFolder <SOGoContactFolder> *folder;
NSString *mail, *domain;
NSSortDescriptor *commonNameDescriptor;
unsigned int i, j, max;
domain = [[context activeUser] domain];
folders = nil;
NS_DURING
{
folders = [self subFolders];
}
NS_HANDLER
{
/* We need to specifically test for @"SOGoDBException", which is
raised explicitly in SOGoParentFolder. Any other exception should
be re-raised. */
if ([[localException name] isEqualToString: @"SOGoDBException"])
folders = nil;
else
[localException raise];
}
NS_ENDHANDLER;
max = [folders count];
sortedFolders = [NSMutableArray arrayWithCapacity: max];
uniqueContacts = [NSMutableDictionary dictionary];
for (i = 0; i < max; i++)
{
folder = [folders objectAtIndex: i];
/* We first search in LDAP folders (in case of duplicated entries in GCS folders) */
if ([folder isKindOfClass: SOGoContactSourceFolderK])
[sortedFolders insertObject: folder atIndex: 0];
else
[sortedFolders addObject: folder];
}
for (i = 0; i < max; i++)
{
folder = [sortedFolders objectAtIndex: i];
//NSLog(@" Address book: %@ (%@)", [folder displayName], [folder class]);
contacts = [folder lookupContactsWithFilter: theFilter
onCriteria: nil
sortBy: @"c_cn"
ordering: NSOrderedAscending
inDomain: domain];
for (j = 0; j < [contacts count]; j++)
{
contact = [contacts objectAtIndex: j];
[contact setObject: [folder nameInContainer]
forKey: @"container"];
mail = [contact objectForKey: @"c_mail"];
if (!excludeLists && [[contact objectForKey: @"c_component"]
isEqualToString: @"vlist"])
[uniqueContacts setObject: contact
forKey: [contact objectForKey: @"c_name"]];
else if ([mail length]
&& [uniqueContacts objectForKey: mail] == nil
&& !(excludeGroups && [contact objectForKey: @"isGroup"]))
[uniqueContacts setObject: contact forKey: mail];
}
}
if ([uniqueContacts count] > 0)
{
// Sort the contacts by display name
commonNameDescriptor = [[NSSortDescriptor alloc] initWithKey: @"c_cn"
ascending: YES];
descriptors = [NSArray arrayWithObjects: commonNameDescriptor, nil];
[commonNameDescriptor release];
sortedContacts = [[uniqueContacts allValues]
sortedArrayUsingDescriptors: descriptors];
}
else
sortedContacts = [NSArray array];
return sortedContacts;
}
- (id<SOGoContactObject>) contactForEmail: (NSString *) theEmail
{
NSArray *allContacts;
NSDictionary *contact;
allContacts = [self allContactsFromFilter: theEmail excludeGroups: YES excludeLists: YES];
contact = [allContacts lastObject];
if (contact)
{
SOGoFolder<SOGoContactObject> *contactObject;
SOGoFolder<SOGoContactFolder> *contactFolder;
contactFolder = [self lookupName: [contact objectForKey: @"container"] inContext: context acquire: NO];
contactObject = [contactFolder lookupName: [contact objectForKey: @"id"] inContext: context acquire: NO];
return contactObject;
}
return nil;
}
- (NSData *) certificateForEmail: (NSString *) theEmail
{
NSDictionary *contact;
NSArray *allContacts;
int i;
allContacts = [self allContactsFromFilter: theEmail excludeGroups: YES excludeLists: YES];
for (i = 0; i < [allContacts count]; i++)
{
contact = [allContacts objectAtIndex: i];
if ([[contact objectForKey: @"c_hascertificate"] boolValue])
{
SOGoFolder<SOGoContactObject> *contactObject;
SOGoFolder<SOGoContactFolder> *contactFolder;
contactFolder = [self lookupName: [contact objectForKey: @"container"] inContext: context acquire: NO];
contactObject = [contactFolder lookupName: [contact objectForKey: @"id"] inContext: context acquire: NO];
return [[contactObject vCard] certificate];
}
}
return nil;
}
@end