/* Copyright (C) 2000-2004 SKYRIX Software AG This file is part of OGo 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. */ // $Id$ #include "AgenorUserManager.h" #include #include "SOGoLRUCache.h" @interface AgenorUserManager (PrivateAPI) - (NGLdapConnection *)ldapConnection; - (void)_cacheCN:(NSString *)_cn forUID:(NSString *)_uid; - (NSString *)_cachedCNForUID:(NSString *)_uid; - (void)_cacheServer:(NSString *)_server forUID:(NSString *)_uid; - (NSString *)_cachedServerForUID:(NSString *)_uid; @end @implementation AgenorUserManager static BOOL debugOn = NO; static BOOL useLDAP = NO; static NSString *ldapHost = nil; static NSString *ldapBaseDN = nil; + (void)initialize { static BOOL didInit = NO; NSUserDefaults *ud; if(didInit) return; didInit = YES; ud = [NSUserDefaults standardUserDefaults]; debugOn = [ud boolForKey:@"SOGoUserManagerDebugEnabled"]; useLDAP = [ud boolForKey:@"SOGoUserManagerUsesLDAP"]; if(useLDAP) { ldapHost = [[ud stringForKey:@"SOGoLDAPHost"] retain]; ldapBaseDN = [[ud stringForKey:@"SOGoLDAPBaseDN"] retain]; } } + (id)sharedUserManager { static id mgr = nil; if(mgr == nil) { mgr = [[self alloc] init]; } return mgr; } - (id)init { self = [super init]; if(self) { self->serverCache = [[SOGoLRUCache alloc] initWithCacheSize:10000]; self->cnCache = [[SOGoLRUCache alloc] initWithCacheSize:10000]; } return self; } - (void)dealloc { [self->serverCache release]; [self->cnCache release]; [super dealloc]; } - (NGLdapConnection *)ldapConnection { static NGLdapConnection *ldapConnection = nil; if(!ldapConnection) { ldapConnection = [[NGLdapConnection alloc] initWithHostName:ldapHost]; #if 0 [ldapConnection setUseCache:YES]; #endif } return ldapConnection; } - (void)_cacheCN:(NSString *)_cn forUID:(NSString *)_uid { [self->cnCache addObject:_cn forKey:_uid]; } - (NSString *)_cachedCNForUID:(NSString *)_uid { return [self->cnCache objectForKey:_uid]; } - (void)_cacheServer:(NSString *)_server forUID:(NSString *)_uid { [self->serverCache addObject:_server forKey:_uid]; } - (NSString *)_cachedServerForUID:(NSString *)_uid { return [self->serverCache objectForKey:_uid]; } /* The uid field is in bijection this the email adress : this field can be construct from the email. Email are uniques. So, we can use email adresse from identifier. The field is made like this : _ if the email is equipement.gouv.fr then the login is the part before the @ for example : fisrtName.lastName _ if the email is not equipement.gouv.fr then the login is the full email adress where @ is change to . (dot) for example : fisrtName.lastName.subDomain.domain.tld NOTE: mapping email -> uid is easy, but can also generate uid's not known to the system (i.e. for private addressbook entries, obvious). The reverse mapping can work _only_ if "firstName.lastname." is guaranteed, because the second dot would be mapped to '@'. This is probably error prone. Only LDAP fetches would guarantee correctness in both cases. */ - (NSString *)getUIDForEmail:(NSString *)_email { NSRange r; NSString *domain; if(!_email || [_email length] == 0) return nil; r = [_email rangeOfString:@"@"]; if(r.length == 0) return nil; domain = [_email substringFromIndex:NSMaxRange(r)]; if(![domain isEqualToString:@"equipement.gouv.fr"]) return _email; return [_email substringToIndex:r.location]; } - (NSString *)getEmailForUID:(NSString *)_uid { NSRange r; if(!_uid || [_uid length] == 0) return nil; r = [_uid rangeOfString:@"@"]; if(r.length > 0) return _uid; return [NSString stringWithFormat:@"%@@equipement.gouv.fr", _uid]; } - (NSString *)getCNForUID:(NSString *)_uid { if(useLDAP) { static NSArray *cnAttrs = nil; NGLdapConnection *conn; EOQualifier *q; NSEnumerator *resultEnum; NGLdapEntry *entry; NGLdapAttribute *cnAttr; NSString *cn; if(!cnAttrs) { cnAttrs = [[NSArray alloc] initWithObjects:@"cn", nil]; } if((cn = [self _cachedCNForUID:_uid])) return cn; q = [EOQualifier qualifierWithQualifierFormat:@"uid = %@", _uid]; conn = [self ldapConnection]; resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN qualifier:q attributes:cnAttrs]; entry = [resultEnum nextObject]; if(!entry) { if(debugOn) { NSLog(@"%s Didn't find LDAP entry for uid '%@'!", __PRETTY_FUNCTION__, _uid); } return nil; } cnAttr = [entry attributeWithName:@"cn"]; if(!cnAttr && debugOn) { NSLog(@"%s LDAP entry for uid '%@' has no common name?", __PRETTY_FUNCTION__, _uid); } cn = [cnAttr stringValueAtIndex:0]; [self _cacheCN:cn forUID:_uid]; return cn; } else { NSString *s; NSRange r; s = _uid; if ([s length] < 10) return s; // TODO: algorithm might be inappropriate, depends on the actual UID r = [s rangeOfString:@"."]; if (r.length == 0) return s; return [s substringToIndex:r.location]; } } - (NSString *)getIMAPAccountStringForUID:(NSString *)_uid { NSString *server; server = [self getServerForUID:_uid]; if(!server) return nil; return [NSString stringWithFormat:@"%@@%@", _uid, server]; } - (NSString *)getServerForUID:(NSString *)_uid { /* First of all : for a particular user IMAP and SMTP are served on the same host. The name of the machine is determined by applying a regex on every values of the mineqMelRoutage LDAP attribute. The regex is : .*%.*@(.*\.melanie2\.i2$) It extracts the substring that follows '@', ends with 'melanie2', on adresses which have a '%' before the '@' Example: helge.hesse%opengroupware.org@servername1.melanie2.i2 -> servername1.melanie2.i2 If only one server name is found by applying the regex on every value of the attribute, then this name is the IMAP/SMTP server for that user. Note that this is the case when we got a unique (well formed) value for the attribute. If the regex finds more than one servername when applied to the differents values, then the IMAP/SMTP server name is to be found in the mineqMelServeurPrincipal attribute of the user. */ if(useLDAP) { static NSArray *attrs = nil; NGLdapConnection *conn; EOQualifier *q; NSEnumerator *resultEnum; NGLdapEntry *entry; NGLdapAttribute *attr; NSMutableArray *serverCandidates; NSString *server; if(!attrs) { attrs = [[NSArray alloc] initWithObjects:@"mineqMelRoutage", @"mineqMelServeurPrincipal", nil]; } if((server = [self _cachedServerForUID:_uid])) return server; q = [EOQualifier qualifierWithQualifierFormat:@"uid = %@", _uid]; conn = [self ldapConnection]; resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN qualifier:q attributes:attrs]; /* we just expect one entry, thus drop the rest */ entry = [resultEnum nextObject]; if(!entry) { if(debugOn) { NSLog(@"%s Didn't find LDAP entry for uid '%@'!", __PRETTY_FUNCTION__, _uid); } return nil; } attr = [entry attributeWithName:@"mineqMelRoutage"]; if(attr) { unsigned i, count; count = [attr count]; serverCandidates = [NSMutableArray arrayWithCapacity:count]; for(i = 0; i < count; i++) { NSRange r; NSString *route; route = [attr stringValueAtIndex:i]; r = [route rangeOfString:@".melanie2.i2" options:NSBackwardsSearch]; if(r.length > 0) { unsigned length; /* be clever */ length = [route length]; r = NSMakeRange(0, length - r.length); r = [route rangeOfString:@"@" options:NSBackwardsSearch range:r]; if(r.length > 0) { unsigned start; NSRange serverNameRange; start = NSMaxRange(r); serverNameRange = NSMakeRange(start, length - start); r = NSMakeRange(0, length - start); r = [route rangeOfString:@"%" options:NSBackwardsSearch range:r]; if(r.length > 0) { NSString *serverName; serverName = [route substringWithRange:serverNameRange]; [serverCandidates addObject:serverName]; } } } } } else { if(debugOn) { NSLog(@"%s LDAP entry for uid '%@' has no mineqMelRoutage entry?", __PRETTY_FUNCTION__, _uid); } serverCandidates = nil; } if([serverCandidates count] == 1) { server = [serverCandidates objectAtIndex:0]; } /* last resort */ if(!server) { attr = [entry attributeWithName:@"mineqMelServeurPrincipal"]; if([attr count] > 0) server = [attr stringValueAtIndex:0]; } if(!server) { if(debugOn) { NSLog(@"%s no chance of getting at server info for user '%@', " @"tried everything. Sorry.", __PRETTY_FUNCTION__, _uid); } return nil; } [self _cacheServer:server forUID:_uid]; return server; } else { return @"mail.opengroupware.org"; } } @end