2010-03-31 22:15:11 +02:00
|
|
|
/* SOGoSockDOperation.m - this file is part of SOGo
|
|
|
|
*
|
|
|
|
* Copyright (C) 2010 Inverse inc.
|
|
|
|
*
|
|
|
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <ldap.h>
|
|
|
|
|
|
|
|
#import <Foundation/NSArray.h>
|
|
|
|
#import <Foundation/NSString.h>
|
|
|
|
|
|
|
|
#import <NGExtensions/NGBase64Coding.h>
|
|
|
|
#import <NGStreams/NGActiveSocket.h>
|
2010-04-23 17:53:54 +02:00
|
|
|
#import <Contacts/SOGoContactFolder.h>
|
2010-03-31 22:15:11 +02:00
|
|
|
#import <Contacts/SOGoContactFolders.h>
|
2011-12-30 21:39:07 +01:00
|
|
|
#import <Contacts/NSString+LDIF.h>
|
2010-03-31 22:15:11 +02:00
|
|
|
#import <SOGo/SOGoProductLoader.h>
|
2010-04-20 23:30:17 +02:00
|
|
|
#import <SOGo/SOGoUserFolder.h>
|
2010-03-31 22:15:11 +02:00
|
|
|
#import <SOGo/NSDictionary+Utilities.h>
|
|
|
|
#import <SOGo/NSString+Utilities.h>
|
|
|
|
|
|
|
|
#import "SOGoSockDOperation.h"
|
|
|
|
|
2010-04-23 18:29:35 +02:00
|
|
|
Class SOGoContactSourceFolderKlass = Nil;
|
|
|
|
|
2010-03-31 22:15:11 +02:00
|
|
|
@interface _SOGoSockDOperationSearch : SOGoSockDOperation
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface _SOGoSockDOperationUnbind : SOGoSockDOperation
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation SOGoSockDOperation
|
|
|
|
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
[[SOGoProductLoader productLoader]
|
|
|
|
loadProducts: [NSArray arrayWithObjects: @"Contacts.SOGo", nil]];
|
2010-04-23 18:29:35 +02:00
|
|
|
SOGoContactSourceFolderKlass
|
|
|
|
= NSClassFromString (@"SOGoContactSourceFolder");
|
2010-03-31 22:15:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (SOGoSockDOperation *) operationWithMethod: (NSString *) method
|
2010-04-20 23:30:17 +02:00
|
|
|
andParameters: (NSDictionary *) opParameters
|
2010-03-31 22:15:11 +02:00
|
|
|
{
|
|
|
|
static NSArray *operations = nil;
|
|
|
|
NSString *className;
|
|
|
|
SOGoSockDOperation *newOperation;
|
|
|
|
|
|
|
|
if (!operations)
|
|
|
|
operations = [[NSArray alloc] initWithObjects:
|
|
|
|
@"SEARCH", @"UNBIND",
|
|
|
|
nil];
|
|
|
|
if ([operations containsObject: method])
|
|
|
|
{
|
|
|
|
className = [NSString stringWithFormat: @"_SOGoSockDOperation%@",
|
|
|
|
[method capitalizedString]];
|
|
|
|
newOperation = [NSClassFromString (className) new];
|
|
|
|
[newOperation autorelease];
|
2010-04-20 23:30:17 +02:00
|
|
|
[newOperation setParameters: opParameters];
|
2010-03-31 22:15:11 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
newOperation = nil;
|
|
|
|
|
|
|
|
return newOperation;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
[parameters release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setParameters: (NSDictionary *) newParameters
|
|
|
|
{
|
|
|
|
ASSIGN (parameters, newParameters);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _appendEntry: (NSDictionary *) entry
|
|
|
|
toResult: (NSMutableString *) result
|
|
|
|
{
|
|
|
|
static NSArray *ldifFields = nil;
|
|
|
|
NSString *key, *value;
|
|
|
|
int count, max;
|
|
|
|
|
|
|
|
if (!ldifFields)
|
|
|
|
{
|
|
|
|
ldifFields = [NSArray arrayWithObjects: @"c_cn", @"c_givenname",
|
2010-04-15 20:26:33 +02:00
|
|
|
@"c_mail", @"c_o", @"c_screenname", @"c_sn",
|
|
|
|
@"c_telephonenumber", nil];
|
2010-03-31 22:15:11 +02:00
|
|
|
[ldifFields retain];
|
|
|
|
}
|
|
|
|
|
2010-04-23 18:29:35 +02:00
|
|
|
if (singleEntryOperation)
|
|
|
|
[result appendFormat: @"dn: %@\n", [parameters objectForKey: @"base"]];
|
|
|
|
else
|
|
|
|
[result appendFormat: @"dn: uid=%@,%@\n", [entry objectForKey: @"c_name"],
|
|
|
|
[parameters objectForKey: @"base"]];
|
2010-03-31 22:15:11 +02:00
|
|
|
[result appendString: @"objectClass: person\nobjectClass: inetOrgPerson\n"];
|
2010-04-23 18:29:35 +02:00
|
|
|
[result appendFormat: @"uid: %@\n", [entry objectForKey: @"c_name"]];
|
2010-03-31 22:15:11 +02:00
|
|
|
|
|
|
|
max = [ldifFields count];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
key = [ldifFields objectAtIndex: count];
|
|
|
|
value = [entry objectForKey: key];
|
|
|
|
if ([value isKindOfClass: [NSString class]] && [value length] > 0)
|
|
|
|
{
|
2011-12-30 21:39:07 +01:00
|
|
|
if ([value mustEncodeLDIFValue])
|
2010-03-31 22:15:11 +02:00
|
|
|
[result appendFormat: @"%@:: %@\n",
|
|
|
|
[key substringFromIndex: 2],
|
|
|
|
[value stringByEncodingBase64]];
|
2011-12-30 21:39:07 +01:00
|
|
|
else
|
|
|
|
[result appendFormat: @"%@: %@\n",
|
|
|
|
[key substringFromIndex: 2], value];
|
2010-03-31 22:15:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
[result appendString: @"\n"];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) respondOnSocket: (NGActiveSocket *) responseSocket
|
|
|
|
{
|
|
|
|
int count, max;
|
|
|
|
NSMutableString *result;
|
|
|
|
|
|
|
|
result = [NSMutableString string];
|
|
|
|
|
|
|
|
max = [resultEntries count];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
[self _appendEntry: [resultEntries objectAtIndex: count]
|
|
|
|
toResult: result];
|
|
|
|
|
|
|
|
[result appendFormat: @"RESULT\ncode: %", resultCode];
|
|
|
|
[responseSocket
|
|
|
|
safeWriteData: [result dataUsingEncoding: NSASCIIStringEncoding]];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation _SOGoSockDOperationSearch
|
|
|
|
|
|
|
|
- (NSString *) _extractFirstValueFromDN: (NSString *) dn
|
|
|
|
{
|
|
|
|
NSString *firstValue;
|
|
|
|
NSRange charRange, valueRange;
|
|
|
|
|
|
|
|
charRange = [dn rangeOfString: @"="];
|
|
|
|
valueRange.location = charRange.location + 1;
|
|
|
|
charRange = [dn rangeOfString: @","];
|
2010-04-23 17:53:54 +02:00
|
|
|
if (charRange.location == NSNotFound)
|
|
|
|
valueRange.length = [dn length] - valueRange.location;
|
|
|
|
else
|
|
|
|
valueRange.length = charRange.location - valueRange.location;
|
2010-03-31 22:15:11 +02:00
|
|
|
firstValue = [dn substringFromRange: valueRange];
|
|
|
|
|
|
|
|
return firstValue;
|
|
|
|
}
|
|
|
|
|
2010-04-23 17:53:54 +02:00
|
|
|
- (NSArray *) _extractIdsFromDN: (NSString *) dn
|
|
|
|
{
|
|
|
|
NSRange suffixRange;
|
|
|
|
NSString *prefix, *identifier;
|
|
|
|
NSMutableArray *ids;
|
|
|
|
NSArray *components;
|
|
|
|
int count, max;
|
|
|
|
|
|
|
|
ids = nil;
|
|
|
|
|
|
|
|
suffixRange = [dn rangeOfString: [parameters objectForKey: @"suffix"]];
|
|
|
|
if (suffixRange.length > 0)
|
|
|
|
{
|
|
|
|
if (suffixRange.location > 0)
|
|
|
|
prefix = [dn substringToIndex: suffixRange.location - 1];
|
|
|
|
else
|
|
|
|
prefix = @"";
|
|
|
|
components = [prefix componentsSeparatedByString: @","];
|
|
|
|
max = [components count];
|
|
|
|
ids = [NSMutableArray arrayWithCapacity: max];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
identifier = [self _extractFirstValueFromDN:
|
|
|
|
[components objectAtIndex: count]];
|
|
|
|
[ids addObject: identifier];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ids;
|
|
|
|
}
|
|
|
|
|
2010-03-31 22:15:11 +02:00
|
|
|
- (NSString *) _convertLDAPFilter: (NSString *) ldapFilter
|
|
|
|
{
|
|
|
|
/* Ultra hackish: we extract the value between the first "=" and the first
|
|
|
|
")", thereby assuming it will be the same for all search fields */
|
|
|
|
NSString *filter;
|
|
|
|
NSRange charRange, valueRange;
|
|
|
|
|
|
|
|
charRange = [ldapFilter rangeOfString: @"="];
|
|
|
|
valueRange.location = charRange.location + 1;
|
|
|
|
charRange = [ldapFilter rangeOfString: @")"];
|
|
|
|
valueRange.length = charRange.location - valueRange.location;
|
|
|
|
filter = [ldapFilter substringFromRange: valueRange];
|
|
|
|
charRange = [filter rangeOfString: @"*"];
|
|
|
|
if (charRange.length == 1)
|
|
|
|
{
|
|
|
|
if (charRange.location == 0)
|
|
|
|
filter = [filter substringFromIndex: 1];
|
|
|
|
else
|
|
|
|
filter = [filter substringToIndex: [filter length] - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
return filter;
|
|
|
|
}
|
|
|
|
|
2010-04-23 17:53:54 +02:00
|
|
|
- (id <SOGoContactFolder>) _getFolderWithId: (NSString *) folderId
|
2010-03-31 22:15:11 +02:00
|
|
|
forUser: (NSString *) uid
|
|
|
|
{
|
|
|
|
SOGoUserFolder *userFolder;
|
|
|
|
SOGoContactFolders *parentFolder;
|
2010-04-23 17:53:54 +02:00
|
|
|
id <SOGoContactFolder> folder;
|
2010-03-31 22:15:11 +02:00
|
|
|
|
|
|
|
userFolder = [SOGoUserFolder objectWithName: uid inContainer: nil];
|
|
|
|
parentFolder = [userFolder lookupName: @"Contacts"
|
|
|
|
inContext: nil acquire: NO];
|
2010-11-04 15:55:41 +01:00
|
|
|
/* Note that this prevent lookup on subscribed addressbooks. */
|
|
|
|
folder = [parentFolder lookupPersonalFolder: folderId
|
|
|
|
ignoringRights: YES];
|
2010-03-31 22:15:11 +02:00
|
|
|
|
|
|
|
return folder;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _performSearch
|
|
|
|
{
|
2010-04-23 17:53:54 +02:00
|
|
|
NSString *bindDN, *baseDN, *uid, *filter;
|
|
|
|
id <SOGoContactFolder> folder;
|
|
|
|
id singleEntry;
|
|
|
|
NSArray *dnIDs;
|
|
|
|
int idCount;
|
2010-03-31 22:15:11 +02:00
|
|
|
|
|
|
|
bindDN = [parameters objectForKey: @"binddn"];
|
|
|
|
baseDN = [parameters objectForKey: @"base"];
|
|
|
|
if ([bindDN length] && [baseDN length])
|
|
|
|
{
|
|
|
|
uid = [self _extractFirstValueFromDN: bindDN];
|
2010-04-23 17:53:54 +02:00
|
|
|
dnIDs = [self _extractIdsFromDN: baseDN];
|
|
|
|
idCount = [dnIDs count];
|
|
|
|
if (idCount && idCount < 3)
|
2010-03-31 22:15:11 +02:00
|
|
|
{
|
2010-04-23 17:53:54 +02:00
|
|
|
folder = [self _getFolderWithId: [dnIDs lastObject] forUser: uid];
|
|
|
|
if (folder)
|
|
|
|
{
|
|
|
|
if (idCount == 1)
|
|
|
|
{
|
2010-04-23 18:29:35 +02:00
|
|
|
singleEntryOperation = NO;
|
2010-04-23 17:53:54 +02:00
|
|
|
filter = [self _convertLDAPFilter:
|
|
|
|
[parameters objectForKey: @"filter"]];
|
2010-04-23 18:29:35 +02:00
|
|
|
if ([filter length] == 0
|
|
|
|
&& [folder isKindOfClass: SOGoContactSourceFolderKlass])
|
|
|
|
filter = @".";
|
2010-04-23 17:53:54 +02:00
|
|
|
resultEntries
|
|
|
|
= [folder lookupContactsWithFilter: filter
|
2010-10-26 22:19:37 +02:00
|
|
|
onCriteria: @"name_or_address"
|
2010-04-23 17:53:54 +02:00
|
|
|
sortBy: @"c_cn"
|
2012-02-11 08:08:02 +01:00
|
|
|
ordering: NSOrderedAscending
|
|
|
|
inDomain: nil];
|
2010-04-23 17:53:54 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-04-23 18:29:35 +02:00
|
|
|
singleEntryOperation = YES;
|
2010-04-23 17:53:54 +02:00
|
|
|
singleEntry = [folder lookupContactWithName:
|
|
|
|
[dnIDs objectAtIndex: 0]];
|
|
|
|
resultEntries = [NSArray arrayWithObject: singleEntry];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
resultCode = LDAP_NO_SUCH_OBJECT;
|
2010-03-31 22:15:11 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
resultCode = LDAP_NO_SUCH_OBJECT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
resultCode = LDAP_PROTOCOL_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) respondOnSocket: (NGActiveSocket *) responseSocket
|
|
|
|
{
|
|
|
|
[self _performSearch];
|
|
|
|
[super respondOnSocket: responseSocket];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation _SOGoSockDOperationUnbind
|
|
|
|
@end
|