From 048985d97651c8f7489b7be6ea9fe96c0bbb7e4c Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Wed, 31 Mar 2010 20:15:11 +0000 Subject: [PATCH] Monotone-Parent: 1386ff575a581ce153a4d70c50dc0bed29d339ed Monotone-Revision: 2f9e650d437ad6f3b4629d6df38c98b8151e4f0f Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2010-03-31T20:15:11 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 4 + Tools/GNUmakefile | 11 +- Tools/SOGoSockD.h | 41 +++++++ Tools/SOGoSockD.m | 167 ++++++++++++++++++++++++++ Tools/SOGoSockDOperation.h | 46 +++++++ Tools/SOGoSockDOperation.m | 239 +++++++++++++++++++++++++++++++++++++ Tools/SOGoSockDScanner.h | 39 ++++++ Tools/SOGoSockDScanner.m | 94 +++++++++++++++ 8 files changed, 640 insertions(+), 1 deletion(-) create mode 100644 Tools/SOGoSockD.h create mode 100644 Tools/SOGoSockD.m create mode 100644 Tools/SOGoSockDOperation.h create mode 100644 Tools/SOGoSockDOperation.m create mode 100644 Tools/SOGoSockDScanner.h create mode 100644 Tools/SOGoSockDScanner.m diff --git a/ChangeLog b/ChangeLog index d28ebf526..2164cd480 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2010-03-31 Wolfgang Sourdeau + * Tools/SOGoSockDOperation.m, Tools/SOGoSockDScanner.m, + Tools/SOGoSockD.m: new modules implementing the sockd backend + tool for slapd. + * Tools/sogo-tool.m (-run): return a proper return code rather than "NO". diff --git a/Tools/GNUmakefile b/Tools/GNUmakefile index 00063777d..73a4f6600 100644 --- a/Tools/GNUmakefile +++ b/Tools/GNUmakefile @@ -15,7 +15,16 @@ $(SOGO_TOOL)_OBJC_FILES += \ SOGoToolCheckDoubles.m \ SOGoToolRemoveDoubles.m \ -TOOL_NAME = $(SOGO_TOOL) +SOGO_SLAPD_SOCKD = sogo-slapd-sockd +$(SOGO_SLAPD_SOCKD)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS) +$(SOGO_SLAPD_SOCKD)_OBJC_FILES += \ + sogo-slapd-sockd.m \ + \ + SOGoSockD.m \ + SOGoSockDScanner.m \ + SOGoSockDOperation.m \ + +TOOL_NAME = $(SOGO_TOOL) $(SOGO_SLAPD_SOCKD) -include GNUmakefile.preamble include $(GNUSTEP_MAKEFILES)/tool.make diff --git a/Tools/SOGoSockD.h b/Tools/SOGoSockD.h new file mode 100644 index 000000000..6abe2ee12 --- /dev/null +++ b/Tools/SOGoSockD.h @@ -0,0 +1,41 @@ +/* SOGoSockD.h - this file is part of SOGo + * + * Copyright (C) 2010 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * 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. + */ + +#ifndef SOGOSOCKD_H +#define SOGOSOCKD_H + +#import + +@class NGPassiveSocket; + +@protocol RunLoopEvents; + +@interface SOGoSockD : NSObject +{ + NGPassiveSocket *listeningSocket; +} + +- (BOOL) run; + +@end + +#endif /* SOGOSOCKD_H */ diff --git a/Tools/SOGoSockD.m b/Tools/SOGoSockD.m new file mode 100644 index 000000000..c8b9bcee3 --- /dev/null +++ b/Tools/SOGoSockD.m @@ -0,0 +1,167 @@ +/* SOGoSockD.m - this file is part of SOGo + * + * Copyright (C) 2010 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * 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 +#import +#import +#import + +#import +#import +#import + +#import + +#import "SOGoSockDScanner.h" +#import "SOGoSockD.h" + +@implementation SOGoSockD + +- (void) _setupListeningSocket +{ + NGLocalSocketAddress *address; + NSString *path; + NSUserDefaults *ud; + + ud = [NSUserDefaults standardUserDefaults]; + path = [ud stringForKey: @"SOGoSLAPDSocketPath"]; + if (!path) + path = @"/var/run/sogo/sogo-sockd.sock"; + + address = [NGLocalSocketAddress addressWithPath: path]; + ASSIGN (listeningSocket, [NGPassiveSocket socketBoundToAddress: address]); + [listeningSocket listenWithBacklog: 5]; + [self logWithFormat: @"listening on %@", path]; +} + +- (BOOL) _startRunLoopWithSocket: (NGPassiveSocket *) socket +{ + NSRunLoop *runLoop; + NSDate *limitDate; + BOOL terminate; + void *fdPtr; + + runLoop = [NSRunLoop currentRunLoop]; +#if LONG_MAX > INT_MAX + fdPtr = (void *) ([socket fileDescriptor] & 0xFFFFFFFFFFFFFFFF); +#else + fdPtr = (void *) [socket fileDescriptor]; +#endif + [runLoop addEvent: fdPtr type: ET_RDESC + watcher: self forMode: NSDefaultRunLoopMode]; + + terminate = NO; + while (!terminate) + { + limitDate = [runLoop limitDateForMode:NSDefaultRunLoopMode]; + [runLoop runMode: NSDefaultRunLoopMode beforeDate: limitDate]; + } + + return YES; +} + +- (BOOL) run +{ + BOOL rc; + + [self _setupListeningSocket]; + if (listeningSocket) + rc = [self _startRunLoopWithSocket: listeningSocket]; + else + rc = NO; + + return rc; +} + +- (BOOL) _handleData: (NSData *) socketData + onSocket: (NGActiveSocket *) responseSocket +{ + NSString *stringData; + SOGoSockDScanner *scanner; + SOGoSockDOperation *operation; + BOOL rc; + + stringData = [[NSString alloc] initWithData: socketData + encoding: NSASCIIStringEncoding]; + if (stringData) + { + scanner = [SOGoSockDScanner scannerWithString: stringData]; + operation = [scanner operation]; + if (operation) + { + rc = YES; + [operation respondOnSocket: responseSocket]; + } + else + rc = NO; + } + else + rc = NO; + + return rc; +} + +- (void) _acceptAndHandle +{ + NGActiveSocket *socket; + char buffer[1024]; + unsigned int count; + NSMutableData *socketData; + BOOL done; + NSAutoreleasePool *pool; + + pool = [NSAutoreleasePool new]; + socketData = [NSMutableData dataWithCapacity: 16384]; + + socket = [listeningSocket accept]; + done = NO; + + while (!done) + { + count = [socket readBytes: buffer count: 1024]; + if (count == NGStreamError) + done = YES; + else + { + if (buffer[count-2] == '\n' && buffer[count-1] == '\n') + { + done = YES; + count -= 2; + } + [socketData appendBytes: buffer length: count]; + [self _handleData: socketData onSocket: socket]; + socketData = [NSMutableData dataWithCapacity: 16384]; + } + } + [pool release]; +} + +- (void) receivedEvent: (void*) data + type: (RunLoopEventType) type + extra: (void*) extra + forMode: (NSString*) mode +{ + if (type == ET_RDESC) + [self _acceptAndHandle]; +} + +@end diff --git a/Tools/SOGoSockDOperation.h b/Tools/SOGoSockDOperation.h new file mode 100644 index 000000000..f6ba745f5 --- /dev/null +++ b/Tools/SOGoSockDOperation.h @@ -0,0 +1,46 @@ +/* SOGoSockDOperation.h - this file is part of SOGo + * + * Copyright (C) 2010 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * 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. + */ + +#ifndef SOGOSOCKDOPERATION_H +#define SOGOSOCKDOPERATION_H + +#import + +@class NGActiveSocket; + +@interface SOGoSockDOperation : NSObject +{ + NSDictionary *parameters; + NSArray *resultEntries; + int resultCode; +} + ++ (SOGoSockDOperation *) operationWithMethod: (NSString *) method + andParameters: (NSDictionary *) newParameters; + +- (void) setParameters: (NSDictionary *) newParameters; + +- (void) respondOnSocket: (NGActiveSocket *) responseSocket; + +@end + +#endif /* SOGOSOCKDOPERATION_H */ diff --git a/Tools/SOGoSockDOperation.m b/Tools/SOGoSockDOperation.m new file mode 100644 index 000000000..2fdc27c12 --- /dev/null +++ b/Tools/SOGoSockDOperation.m @@ -0,0 +1,239 @@ +/* SOGoSockDOperation.m - this file is part of SOGo + * + * Copyright (C) 2010 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * 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 + +#import +#import + +#import +#import +#import +#import +#import +#import +#import + +#import "SOGoSockDOperation.h" + +@interface _SOGoSockDOperationSearch : SOGoSockDOperation +@end + +@interface _SOGoSockDOperationUnbind : SOGoSockDOperation +@end + +@implementation SOGoSockDOperation + ++ (void) initialize +{ + [[SOGoProductLoader productLoader] + loadProducts: [NSArray arrayWithObjects: @"Contacts.SOGo", nil]]; +} + ++ (SOGoSockDOperation *) operationWithMethod: (NSString *) method + andParameters: (NSDictionary *) parameters +{ + 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]; + [newOperation setParameters: parameters]; + } + 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", + @"c_mail", @"c_o", @"c_screenname", @"c_sn", nil]; + [ldifFields retain]; + } + + [result appendFormat: @"dn: cn=%@,%@\n", + [entry objectForKey: @"c_name"], [parameters objectForKey: @"base"]]; + [result appendString: @"objectClass: person\nobjectClass: inetOrgPerson\n"]; + + 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) + { + if ([value _isLDIFSafe]) + [result appendFormat: @"%@: %@\n", + [key substringFromIndex: 2], value]; + else + [result appendFormat: @"%@:: %@\n", + [key substringFromIndex: 2], + [value stringByEncodingBase64]]; + } + } + [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: @","]; + valueRange.length = charRange.location - valueRange.location; + firstValue = [dn substringFromRange: valueRange]; + + return firstValue; +} + +- (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; +} + +- (SOGoContactGCSFolder *) _getFolderWithId: (NSString *) folderId + forUser: (NSString *) uid +{ + SOGoUserFolder *userFolder; + SOGoContactFolders *parentFolder; + SOGoContactGCSFolder *folder; + + userFolder = [SOGoUserFolder objectWithName: uid inContainer: nil]; + parentFolder = [userFolder lookupName: @"Contacts" + inContext: nil acquire: NO]; + folder = [parentFolder lookupName: folderId inContext: nil acquire: NO]; + + return folder; +} + +- (void) _performSearch +{ + NSString *bindDN, *baseDN, *uid, *folderId, *filter; + SOGoContactGCSFolder *folder; + + bindDN = [parameters objectForKey: @"binddn"]; + baseDN = [parameters objectForKey: @"base"]; + if ([bindDN length] && [baseDN length]) + { + uid = [self _extractFirstValueFromDN: bindDN]; + folderId = [self _extractFirstValueFromDN: baseDN]; + folder = [self _getFolderWithId: folderId forUser: uid]; + if (folder) + { + filter + = [self _convertLDAPFilter: [parameters objectForKey: @"filter"]]; + // if (![filter length] + // && [folder isKindOfClass: [SOGoContactSourceFolder class]]) + // filter = @"*"; + resultEntries + = [folder lookupContactsWithFilter: filter + sortBy: @"c_cn" + ordering: NSOrderedAscending]; + } + else + resultCode = LDAP_NO_SUCH_OBJECT; + } + else + resultCode = LDAP_PROTOCOL_ERROR; +} + +- (void) respondOnSocket: (NGActiveSocket *) responseSocket +{ + [self _performSearch]; + [super respondOnSocket: responseSocket]; +} + +@end + +@implementation _SOGoSockDOperationUnbind +@end diff --git a/Tools/SOGoSockDScanner.h b/Tools/SOGoSockDScanner.h new file mode 100644 index 000000000..e9f2ffe4e --- /dev/null +++ b/Tools/SOGoSockDScanner.h @@ -0,0 +1,39 @@ +/* SOGoSockDScanner.h - this file is part of SOGo + * + * Copyright (C) 2010 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * 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. + */ + +#ifndef SOGOSOCKDSCANNER_H +#define SOGOSOCKDSCANNER_H + +#import + +#import "SOGoSockDOperation.h" + +@interface SOGoSockDScanner : NSScanner +{ + SOGoSockDOperation *operation; +} + +- (SOGoSockDOperation *) operation; + +@end + +#endif /* SOGOSOCKDSCANNER_H */ diff --git a/Tools/SOGoSockDScanner.m b/Tools/SOGoSockDScanner.m new file mode 100644 index 000000000..611549005 --- /dev/null +++ b/Tools/SOGoSockDScanner.m @@ -0,0 +1,94 @@ + +/* SOGoSockDScanner.m - this file is part of SOGo + * + * Copyright (C) 2010 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * 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 + +#import "SOGoSockDOperation.h" + +#import "SOGoSockDScanner.h" + +@implementation SOGoSockDScanner + +- (id) init +{ + if ((self = [super init])) + { + operation = nil; + } + + return self; +} + +- (void) _parseRequest +{ + NSString *method, *key, *value; + NSMutableArray *multiValue; + NSMutableDictionary *parameters; + NSUInteger maxLocation; + BOOL error; + + /* method */ + if ([self scanUpToString: @"\n" intoString: &method]) + { + parameters = [NSMutableDictionary new]; + error = NO; + maxLocation = [[self string] length] - 1; + while (!error && [self scanLocation] < maxLocation) + { + if ([self scanUpToString: @":" intoString: &key] + && [self scanUpToString: @"\n" intoString: &value]) + { + if ([value hasPrefix: @": "]) + value = [value substringFromIndex: 2]; + multiValue = [parameters objectForKey: key]; + if (multiValue) + { + if (![multiValue isKindOfClass: [NSArray class]]) + { + multiValue = [NSMutableArray arrayWithObject: multiValue]; + [parameters setObject: multiValue forKey: key]; + } + [multiValue addObject: value]; + } + else + [parameters setObject: value forKey: key]; + } + else + error = YES; + } + if (!error) + operation = [SOGoSockDOperation operationWithMethod: method + andParameters: parameters]; + [parameters release]; + } +} + +- (SOGoSockDOperation *) operation +{ + if (!operation) + [self _parseRequest]; + + return operation; +} + +@end