diff --git a/ChangeLog b/ChangeLog index 371e3a5c7..3855edc51 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2011-12-30 Wolfgang Sourdeau + * SoObjects/SOGo/LDAPSourceSchema.[hm]: new class module enabling + schema auto-discovery. + * UI/WebServerResources/UIxContactEditor.js (validateContactEditor): the birth date is validated slightly differently, by enabling empty and 2-digit years as well as single diff --git a/SoObjects/SOGo/LDAPSourceSchema.h b/SoObjects/SOGo/LDAPSourceSchema.h new file mode 100644 index 000000000..13d415bae --- /dev/null +++ b/SoObjects/SOGo/LDAPSourceSchema.h @@ -0,0 +1,45 @@ +/* LDAPSourceSchema.h - this file is part of SOGo + * + * Copyright (C) 2011 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 LDAPSOURCESCHEMA_H +#define LDAPSOURCESCHEMA_H + +#import + +@class NSMutableDictionary; +@class NGLdapConnection; + +@interface LDAPSourceSchema : NSObject +{ + NSMutableDictionary *schema; +} + +- (void) readSchemaFromConnection: (NGLdapConnection *) conn; + +- (NSArray *) fieldsForClass: (NSString *) className; + +/* merged list of attributes with unique names */ +- (NSArray *) fieldsForClasses: (NSArray *) className; + +@end + +#endif /* LDAPSOURCESCHEMA_H */ diff --git a/SoObjects/SOGo/LDAPSourceSchema.m b/SoObjects/SOGo/LDAPSourceSchema.m new file mode 100644 index 000000000..a81417782 --- /dev/null +++ b/SoObjects/SOGo/LDAPSourceSchema.m @@ -0,0 +1,295 @@ +/* LDAPSourceSchema.m - this file is part of SOGo + * + * Copyright (C) 2011 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 "LDAPSourceSchema.h" +#import "NSDictionary+Utilities.h" + +static EOQualifier *allOCQualifier = nil; + +@implementation LDAPSourceSchema + ++ (void) initialize +{ + allOCQualifier = [[EOKeyValueQualifier alloc] + initWithKey: @"objectClass" + operatorSelector: EOQualifierOperatorEqual + value: @"*"]; +} + +- (id) init +{ + if ((self = [super init])) + { + schema = nil; + } + + return self; +} + +- (void) dealloc +{ + [schema release]; + [super dealloc]; +} + +static NSArray * +schemaTokens (NSString *schema) +{ + unichar *characters; + NSUInteger count, max, parenLevel = 0, firstChar = (NSUInteger) -1; + NSMutableArray *arrayString, *parentArray, *currentArray = nil; + NSArray *topArray = nil; + NSString *token; + + arrayString = [NSMutableArray array]; + + max = [schema length]; + characters = malloc ((max + 1) * sizeof (unichar)); + characters[max] = 0; + [schema getCharacters: characters]; + + for (count = 0; count < max; count++) + { + switch (characters[count]) + { + case '(': + // NSLog (@"increase"); + parenLevel++; + parentArray = currentArray; + currentArray = [NSMutableArray array]; + if (parentArray == nil) + topArray = currentArray; + [parentArray addObject: currentArray]; + [arrayString addObject: currentArray]; + break; + case ')': + // NSLog (@"decrease"); + parenLevel--; + [arrayString removeLastObject]; + currentArray = [arrayString lastObject]; + break; + case ' ': + if (firstChar != (NSUInteger) -1) + { + token = [NSString stringWithCharacters: characters + firstChar + length: (count - firstChar)]; + if (![token isEqualToString: @"$"]) + [currentArray addObject: token]; + // NSLog (@"added token: %@", token); + firstChar = (NSUInteger) -1; + } + break; + default: + if (currentArray && (firstChar == (NSUInteger) -1)) + firstChar = count; + } + } + + free (characters); + + return topArray; +} + +static inline id +schemaValue (NSArray *tokens, NSString *key) +{ + NSUInteger idx; + id value; + + idx = [tokens indexOfObject: key]; + if (idx != NSNotFound) + value = [tokens objectAtIndex: (idx + 1)]; + else + value = nil; + + return value; +} + +static NSMutableDictionary * +parseSchema (NSString *schema) +{ + NSArray *tokens; + NSMutableDictionary *schemaDict; + NSMutableArray *fields; + id value; + + schemaDict = [NSMutableDictionary dictionaryWithCapacity: 6]; + tokens = schemaTokens (schema); + // [schemaDict setObject: [tokens objectAtIndex: 0] + // forKey: @"oid"]; + value = schemaValue (tokens, @"NAME"); + if (value) + { + /* sometimes, objectClasses can have two names */ + if ([value isKindOfClass: [NSString class]]) + value = [NSArray arrayWithObject: value]; + [schemaDict setObject: value forKey: @"names"]; + } + + value = schemaValue (tokens, @"SUP"); + if (value) + [schemaDict setObject: value forKey: @"sup"]; + + fields = [NSMutableArray new]; + [schemaDict setObject: fields forKey: @"fields"]; + [fields release]; + value = schemaValue (tokens, @"MUST"); + if (value) + { + if ([value isKindOfClass: [NSArray class]]) + [fields addObjectsFromArray: value]; + else + [fields addObject: value]; + } + value = schemaValue (tokens, @"MAY"); + if (value) + { + if ([value isKindOfClass: [NSArray class]]) + [fields addObjectsFromArray: value]; + else + [fields addObject: value]; + } + + return schemaDict; +} + +static void +fillSchemaFromEntry (NSMutableDictionary *schema, NGLdapEntry *entry) +{ + NSEnumerator *strings; + NGLdapAttribute *attr; + NSMutableDictionary *schemaDict; + NSArray *names; + NSString *string, *name; + NSUInteger count, max; + + attr = [entry attributeWithName: @"objectclasses"]; + strings = [attr stringValueEnumerator]; + while ((string = [strings nextObject])) + { + schemaDict = parseSchema (string); + names = [schemaDict objectForKey: @"names"]; + max = [names count]; + for (count = 0; count < max; count++) + { + name = [[names objectAtIndex: count] lowercaseString]; + if ([name hasPrefix: @"'"] && [name hasSuffix: @"'"]) + name + = [name substringWithRange: NSMakeRange (1, [name length] - 2)]; + [schema setObject: schemaDict forKey: name]; + } + /* the list of names is no longer required from the schema itself */ + [schemaDict removeObjectForKey: @"names"]; + } +} + +- (void) readSchemaFromConnection: (NGLdapConnection *) conn +{ + NSEnumerator *entries; + NGLdapEntry *entry; + NSString *dn; + + ASSIGN (schema, [NSMutableDictionary new]); + [schema release]; + + entries = [conn baseSearchAtBaseDN: @"" + qualifier: allOCQualifier + attributes: [NSArray arrayWithObject: @"subschemaSubentry"]]; + entry = [entries nextObject]; + if (entry) + { + dn = [[entry attributeWithName: @"subschemaSubentry"] + stringValueAtIndex: 0]; + if (dn) + { + entries = [conn baseSearchAtBaseDN: dn + qualifier: allOCQualifier + attributes: [NSArray arrayWithObject: @"objectclasses"]]; + entry = [entries nextObject]; + if (entry) + fillSchemaFromEntry (schema, entry); + } + } +} + +static void +fillFieldsForClass (NSMutableDictionary *schema, NSString *schemaName, + NSMutableArray *fields) +{ + NSDictionary *schemaDict; + NSString *sup; + NSArray *schemaFields; + + schemaDict = [schema objectForKey: [schemaName lowercaseString]]; + if (schemaDict) + { + schemaFields = [schemaDict objectForKey: @"fields"]; + if ([schemaFields count] > 0) + [fields addObjectsFromArray: schemaFields]; + sup = [schemaDict objectForKey: @"sup"]; + if ([sup length] > 0) + fillFieldsForClass (schema, sup, fields); + } +} + +- (NSArray *) fieldsForClass: (NSString *) className +{ + NSMutableArray *fields; + + fields = [NSMutableArray arrayWithCapacity: 128]; + fillFieldsForClass (schema, className, fields); + + return fields; +} + +- (NSArray *) fieldsForClasses: (NSArray *) classNames +{ + NSMutableDictionary *fieldHash; + NSNumber *yesValue; + NSString *name; + NSUInteger count, max; + + yesValue = [NSNumber numberWithBool: YES]; + + fieldHash = [NSMutableDictionary dictionary]; + max = [classNames count]; + for (count = 0; count < max; count++) + { + name = [classNames objectAtIndex: count]; + [fieldHash setObject: yesValue forKeys: [self fieldsForClass: name]]; + } + + return [fieldHash allKeys]; +} + +@end