]> err.no Git - scalable-opengroupware.org/commitdiff
refactoring in the LDAP code of AgenorUserManager
authorhelge <helge@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Sun, 20 Feb 2005 22:53:09 +0000 (22:53 +0000)
committerhelge <helge@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Sun, 20 Feb 2005 22:53:09 +0000 (22:53 +0000)
git-svn-id: http://svn.opengroupware.org/SOGo/trunk@591 d1b88da0-ebda-0310-925b-ed51d893ca5b

OGoContentStore/NSURL+OCS.h
SOGo/SoObjects/Mailer/SOGoMailManager.m
SOGo/SoObjects/SOGo/AgenorUserManager.m
SOGo/SoObjects/SOGo/ChangeLog
SOGo/SoObjects/SOGo/Version

index 1d0c204b8d97c62a812f5b369f60b594eca17e21..ca7319c75bf45aeeaaaa46e21d9165483314b76d 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2004 SKYRIX Software AG
+  Copyright (C) 2004-2005 SKYRIX Software AG
 
   This file is part of OpenGroupware.org.
 
@@ -18,7 +18,6 @@
   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */
-// $Id$
 
 #ifndef __OGoContentStore_NSURL_OCS_H__
 #define __OGoContentStore_NSURL_OCS_H__
index cf7ddb58d16f988e906cb03dc43c305e2281b204..03d44a7bf0c7b86cf44b0448e1837c58096b2e7e 100644 (file)
@@ -191,9 +191,13 @@ static NSString       *imap4Separator  = nil;
   result = [client login:[_url user] password:_pwd];
   if (![[result valueForKey:@"result"] boolValue]) {
     [self errorWithFormat:
-            @"IMAP4 login failed (host=%@,user=%@,pwd=%s,url=%@/%@/%@): %@", 
+            @"IMAP4 login failed:\n"
+           @"  host=%@, user=%@, pwd=%s\n"
+           @"  url=%@\n  base=%@\n  base-class=%@)\n"
+           "  = %@", 
             [_url host], [_url user], [_pwd length] > 0 ? "yes" : "no", 
-            [_url absoluteString], [_url baseURL],
+           [_url absoluteString],
+           [_url baseURL],
             NSStringFromClass([[_url baseURL] class]),
             client];
     return nil;
index f44dfdeabf7e7b43008fd3e71a10455d2a69c751..7702a9d22444f9369e7d8e7573668bb7c74e0c3d 100644 (file)
@@ -1,7 +1,7 @@
 /*
-  Copyright (C) 2000-2004 SKYRIX Software AG
+  Copyright (C) 2004-2005 SKYRIX Software AG
 
-  This file is part of OGo
+  This file is part of OpenGroupware.org.
 
   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
@@ -18,8 +18,6 @@
   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */
-// $Id$
-
 
 #include "AgenorUserManager.h"
 #include <NGExtensions/NGExtensions.h>
@@ -47,28 +45,39 @@ static BOOL     debugOn     = NO;
 static BOOL     useLDAP     = NO;
 static NSString *ldapHost   = nil;
 static NSString *ldapBaseDN = nil;
+static NSString *fallbackIMAP4Server = nil;
+static NSString *defaultMailDomain   = @"equipement.gouv.fr";
 
 + (void)initialize {
   static BOOL didInit = NO;
   NSUserDefaults *ud;
 
-  if(didInit)
+  if (didInit)
     return;
   didInit = YES;
-  ud = [NSUserDefaults standardUserDefaults];
+  ud      = [NSUserDefaults standardUserDefaults];
   debugOn = [ud boolForKey:@"SOGoUserManagerDebugEnabled"];
+  
   useLDAP = [ud boolForKey:@"SOGoUserManagerUsesLDAP"];
-  if(useLDAP) {
-    ldapHost   = [[ud stringForKey:@"SOGoLDAPHost"] retain];
-    ldapBaseDN = [[ud stringForKey:@"SOGoLDAPBaseDN"] retain];
+  if (useLDAP) {
+    ldapHost   = [[ud stringForKey:@"SOGoLDAPHost"]   copy];
+    ldapBaseDN = [[ud stringForKey:@"SOGoLDAPBaseDN"] copy];
+    NSLog(@"Note: using LDAP host to manage accounts: %@", ldapHost);
   }
+  else
+    NSLog(@"Note: LDAP access is disabled.");
+  
+  fallbackIMAP4Server = [[ud stringForKey:@"SOGoFallbackIMAP4Server"] copy];
+  if ([fallbackIMAP4Server length] > 0)
+    NSLog(@"Note: using fallback IMAP4 server: '%@'", fallbackIMAP4Server);
+  else
+    fallbackIMAP4Server = nil;
 }
 
 + (id)sharedUserManager {
-  static id mgr = nil;
-  if(mgr == nil) {
+  static AgenorUserManager *mgr = nil;
+  if (mgr == nil)
     mgr = [[self alloc] init];
-  }
   return mgr;
 }
 
@@ -104,35 +113,36 @@ static NSString *ldapBaseDN = nil;
 
 
 /* private cache helpers */
+// TODO: this is really unnecessary, no?
 
 - (void)_cacheCN:(NSString *)_cn forUID:(NSString *)_uid {
+  if (_cn == nil) return;
   [self->cnCache addObject:_cn forKey:_uid];
 }
-
 - (NSString *)_cachedCNForUID:(NSString *)_uid {
   return [self->cnCache objectForKey:_uid];
 }
 
 - (void)_cacheServer:(NSString *)_server forUID:(NSString *)_uid {
+  if (_server == nil) return;
   [self->serverCache addObject:_server forKey:_uid];
 }
-
 - (NSString *)_cachedServerForUID:(NSString *)_uid {
   return [self->serverCache objectForKey:_uid];
 }
 
 - (void)_cacheEmail:(NSString *)_email forUID:(NSString *)_uid {
+  if (_email == nil) return;
   [self->emailCache addObject:_email forKey:_uid];
 }
-
 - (NSString *)_cachedEmailForUID:(NSString *)_uid {
   return [self->emailCache objectForKey:_uid];
 }
 
 - (void)_cacheUID:(NSString *)_uid forEmail:(NSString *)_email {
+  if (_uid == nil) return;
   [self->uidCache addObject:_uid forKey:_email];
 }
-
 - (NSString *)_cachedUIDForEmail:(NSString *)_email {
   return [self->uidCache objectForKey:_email];
 }
@@ -167,8 +177,7 @@ static NSString *ldapBaseDN = nil;
        Only LDAP fetches would guarantee correctness in both cases.
 */
 
-- (NSString *)getUIDForEmail:(NSString *)_email {
-  if(useLDAP) {
+- (NSString *)primaryGetAgenorUIDForEmail:(NSString *)_email {
     static NSArray   *uidAttrs = nil;
     NGLdapConnection *conn;
     EOQualifier      *q;
@@ -181,9 +190,6 @@ static NSString *ldapBaseDN = nil;
       uidAttrs = [[NSArray alloc] initWithObjects:@"uid", nil];
     }
     
-    if((uid = [self _cachedUIDForEmail:_email]))
-      return uid;
-    
     q = [EOQualifier qualifierWithQualifierFormat:@"mail = %@", _email];
     
     conn       = [self ldapConnection];
@@ -191,7 +197,7 @@ static NSString *ldapBaseDN = nil;
                        qualifier:q
                        attributes:uidAttrs];
     entry = [resultEnum nextObject];
-    if(!entry) {
+    if (entry == nil) {
       if(debugOn) {
         [self logWithFormat:@"%s Didn't find LDAP entry for email '%@'!",
                               __PRETTY_FUNCTION__,
@@ -203,8 +209,17 @@ static NSString *ldapBaseDN = nil;
     if (!uidAttr)
       return nil; /* can happen, not unlikely */
     uid = [uidAttr stringValueAtIndex:0];
-    [self _cacheUID:uid forEmail:_email];
     return uid;
+}
+
+- (NSString *)getUIDForEmail:(NSString *)_email {
+  NSString *uid;
+  
+  if ((uid = [self _cachedUIDForEmail:_email]) != nil)
+    return uid;
+    
+  if (useLDAP) {
+    uid = [self primaryGetAgenorUIDForEmail:_email];
   }
   else {
     NSRange r;
@@ -214,58 +229,56 @@ static NSString *ldapBaseDN = nil;
       return nil;
     
     r = [_email rangeOfString:@"@"];
-    if(r.length == 0)
+    if (r.length == 0)
       return nil;
+    
     domain = [_email substringFromIndex:NSMaxRange(r)];
-    if(![domain isEqualToString:@"equipement.gouv.fr"])
-      return _email;
-    return [_email substringToIndex:r.location];
+    if (![domain isEqualToString:defaultMailDomain])
+      uid = _email;
+    else
+      uid = [_email substringToIndex:r.location];
   }
+  
+  [self _cacheUID:uid forEmail:_email];
+  return uid;
 }
 
-- (NSString *)getEmailForUID:(NSString *)_uid {
-  if(useLDAP) {
-    static NSArray   *emailAttrs = nil;
-    NGLdapConnection *conn;
-    EOQualifier      *q;
-    NSEnumerator     *resultEnum;
-    NGLdapEntry      *entry;
-    NGLdapAttribute  *emailAttr;
-    NSString         *email;
-    
-    if(!emailAttrs) {
-      emailAttrs = [[NSArray alloc] initWithObjects:@"mineqMelmailEmission", nil];
-    }
+- (NSString *)primaryGetEmailForAgenorUID:(NSString *)_uid {
+  static NSArray   *emailAttrs = nil;
+  NGLdapConnection *conn;
+  EOQualifier      *q;
+  NSEnumerator     *resultEnum;
+  NGLdapEntry      *entry;
+  NGLdapAttribute  *emailAttr;
+  NSString         *email;
+  unsigned         count;
     
-    if((email = [self _cachedEmailForUID:_uid]))
-      return email;
+  if (emailAttrs == nil)
+    emailAttrs = [[NSArray alloc] initWithObjects:@"mineqMelmailEmission",nil];
     
-    q = [EOQualifier qualifierWithQualifierFormat:@"uid = %@", _uid];
+  q = [EOQualifier qualifierWithQualifierFormat:@"uid = %@", _uid];
     
-    conn       = [self ldapConnection];
-    resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN
-                       qualifier:q
-                       attributes:emailAttrs];
-    entry = [resultEnum nextObject];
-    if(!entry) {
+  conn       = [self ldapConnection];
+  resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN
+                    qualifier:q
+                    attributes:emailAttrs];
+  entry = [resultEnum nextObject];
+  if (entry == nil) {
       if(debugOn) {
         [self logWithFormat:@"%s Didn't find LDAP entry for uid '%@'!",
                               __PRETTY_FUNCTION__,
                               _uid];
       }
       return nil;
-    }
-    emailAttr = [entry attributeWithName:@"mineqMelmailEmission"];
-    if (!emailAttr) {
-      return nil; /* shit happens */
-    }
-    else {
-      unsigned count;
-      
-      email = nil;
-      count = [emailAttr count];
+  }
+  emailAttr = [entry attributeWithName:@"mineqMelmailEmission"];
+  if (emailAttr == nil)
+    return nil; /* shit happens */
+
+  email = nil;
+  count = [emailAttr count];
 #if 0
-      if (count > 1) {
+  if (count > 1) {
         unsigned i;
 
         /* in case there are multiple email addresses, select the first
@@ -274,77 +287,94 @@ static NSString *ldapBaseDN = nil;
           NSString *candidate;
           
           candidate = [emailAttr stringValueAtIndex:i];
-          if (![candidate hasSuffix:@"@equipement.gouv.fr"]) {
+          if (![candidate hasSuffix:defaultMailDomain]) {
+           // TODO: also check for '@'
             email = candidate;
             break;
           }
         }
-      }
+  }
 #endif
-      if (email == nil && count > 0) {
-        email = [emailAttr stringValueAtIndex:0];
-      }
-      [self _cacheEmail:email forUID:_uid];
-      return email;
-    }
+  if (email == nil && count > 0)
+    email = [emailAttr stringValueAtIndex:0];
+  
+  return email;
+}
+
+- (NSString *)getEmailForUID:(NSString *)_uid {
+  NSString *email;
+  
+  if (![_uid isNotNull] || [_uid length] == 0)
+    return nil;
+  if ((email = [self _cachedEmailForUID:_uid]) != nil)
+    return email;
+  
+  if (useLDAP) {
+    email = [self primaryGetEmailForAgenorUID:_uid];
   }
   else {
     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];
+    email = (r.length > 0)
+      ? _uid
+      : [[_uid stringByAppendingString:@"@"]
+              stringByAppendingString:defaultMailDomain];
   }
+  
+  [self _cacheEmail:email forUID:_uid];
+  return email;
 }
 
 
 /* CN */
 
-- (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];
+- (NSString *)primaryGetCNForAgenorUID:(NSString *)_uid {
+  static NSArray   *cnAttrs = nil;
+  NGLdapConnection *conn;
+  EOQualifier      *q;
+  NSEnumerator     *resultEnum;
+  NGLdapEntry      *entry;
+  NGLdapAttribute  *cnAttr;
+  NSString         *cn;
+
+  if (cnAttrs == nil)
+    cnAttrs = [[NSArray alloc] initWithObjects:@"cn", nil];
+  
+  q = [EOQualifier qualifierWithQualifierFormat:@"uid = %@", _uid];
     
-    conn = [self ldapConnection];
-    resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN
-                       qualifier:q
-                       attributes:cnAttrs];
-    entry = [resultEnum nextObject];
-    if(!entry) {
+  conn = [self ldapConnection];
+  resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN
+                    qualifier:q
+                    attributes:cnAttrs];
+  entry = [resultEnum nextObject];
+  if (entry == nil) {
       if(debugOn) {
         [self logWithFormat:@"%s Didn't find LDAP entry for uid '%@'!",
                               __PRETTY_FUNCTION__,
                               _uid];
       }
       return nil;
-    }
-    cnAttr = [entry attributeWithName:@"cn"];
-    if(!cnAttr && debugOn) {
+  }
+  cnAttr = [entry attributeWithName:@"cn"];
+  if(cnAttr == nil && debugOn) {
       [self logWithFormat:@"%s LDAP entry for uid '%@' has no common name?",
                             __PRETTY_FUNCTION__,
                             _uid];
       return nil; /* nothing we can do about it */
-    }
-    cn = [cnAttr stringValueAtIndex:0];
-    [self _cacheCN:cn forUID:_uid];
+  }
+  cn = [cnAttr stringValueAtIndex:0];
+  return cn;
+}
+
+- (NSString *)getCNForUID:(NSString *)_uid {
+  NSString *cn;
+
+  if ((cn = [self _cachedCNForUID:_uid]) != nil)
     return cn;
+  
+  if (useLDAP) {
+    cn = [self primaryGetCNForAgenorUID:_uid];
   }
   else {
     NSString *s;
@@ -357,10 +387,13 @@ static NSString *ldapBaseDN = nil;
     // TODO: algorithm might be inappropriate, depends on the actual UID
     r = [s rangeOfString:@"."];
     if (r.length == 0)
-      return s;
-    
-    return [s substringToIndex:r.location];
+      cn = s;
+    else
+      cn = [s substringToIndex:r.location];
   }
+  
+  [self _cacheCN:cn forUID:_uid];
+  return cn;
 }
 
 
@@ -370,70 +403,47 @@ static NSString *ldapBaseDN = nil;
   NSString *server;
   
   server = [self getServerForUID:_uid];
-  if(!server)
+  if (server == nil)
     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) {
+- (NGLdapEntry *)_fetchEntryForAgenorUID:(NSString *)_uid {
+  static NSArray   *attrs = nil;
+  NGLdapConnection *conn;
+  EOQualifier      *q;
+  NSEnumerator     *resultEnum;
+  NGLdapEntry      *entry;
+  
+  if (attrs == nil) {
+    attrs = [[NSArray alloc] initWithObjects:
+                              @"mineqMelRoutage", 
+                              @"mineqMelServeurPrincipal",
+                            nil];
+  }
+  
+  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 == nil) {
       if(debugOn) {
         NSLog(@"%s Didn't find LDAP entry for uid '%@'!",
               __PRETTY_FUNCTION__,
               _uid);
       }
       return nil;
-    }
-    attr = [entry attributeWithName:@"mineqMelRoutage"];
-    if(attr) {
+  }
+  return entry;
+}
+
+- (NSArray *)_serverCandidatesForMineqMelRoutage:(NGLdapAttribute *)attr {
+  NSMutableArray *serverCandidates;
+
       unsigned i, count;
       
       count = [attr count];
@@ -468,42 +478,101 @@ static NSString *ldapBaseDN = nil;
           }
         }
       }
-    }
-    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];
-    }
+      return serverCandidates;
+}
 
-    /* last resort */
-    if(!server) {
+- (NSString *)primaryGetServerForAgenorUID:(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.
+   */
+  NSString        *server;
+  NGLdapEntry     *entry;
+  NGLdapAttribute *attr;
+
+  if ((entry = [self _fetchEntryForAgenorUID:_uid]) == nil)
+    return nil;
+  
+  attr = [entry attributeWithName:@"mineqMelRoutage"];
+  if (attr != nil) {
+    NSArray *serverCandidates;
+    
+    serverCandidates = [self _serverCandidatesForMineqMelRoutage:attr];
+    
+    if ([serverCandidates count] > 0)
+      server = [serverCandidates objectAtIndex:0];
+  }
+  else {
+    [self debugWithFormat:
+           @"%s LDAP entry for uid '%@' has no mineqMelRoutage entry?",
+            __PRETTY_FUNCTION__,
+            _uid];
+  }
+  
+  /* last resort */
+  if (server == nil) {
       attr = [entry attributeWithName:@"mineqMelServeurPrincipal"];
-      if([attr count] > 0)
+      if ([attr count] > 0)
         server = [attr stringValueAtIndex:0];
-    }
-
-    if(!server) {
-      if(debugOn) {
-        NSLog(@"%s no chance of getting at server info for user '%@', "
+  }
+    
+  if (server == nil) {
+    [self debugWithFormat:
+             @"%s no chance of getting at server info for user '%@', "
               @"tried everything. Sorry.",
               __PRETTY_FUNCTION__,
-              _uid);
-      }
-      return nil;
-    }
+              _uid];
+    return nil;
+  }
+  return server;
+}
+
+- (NSString *)getServerForUID:(NSString *)_uid {
+  NSString *server;
 
-    [self _cacheServer:server forUID:_uid];
+  if (_uid == nil || [_uid length] == 0)
+    return nil;
+  
+  if ((server = [self _cachedServerForUID:_uid]) != nil)
     return server;
-  }
+  
+  if (useLDAP)
+    server = [self primaryGetServerForAgenorUID:_uid];
+  else if (fallbackIMAP4Server)
+    server = fallbackIMAP4Server;
   else {
-    return @"mail.opengroupware.org";
+    [self logWithFormat:@"ERROR: could not get server for uid '%@', "
+         @"neither LDAP (SOGoUserManagerUsesLDAP) nor "
+         @"a fallback (SOGoFallbackIMAP4Server) is configured.",
+         _uid];
+    server = nil;
   }
+  
+  [self _cacheServer:server forUID:_uid];
+  return server;
 }
 
-@end
+/* debugging */
+
+- (BOOL)isDebuggingEnabled {
+  return debugOn;
+}
+
+@end /* AgenorUserManager */
index ef68cad0ee11c4b7b35397085a30df00602a3cae..91b1668fad2b9cedc81d19d396d5529767f5858c 100644 (file)
@@ -1,3 +1,9 @@
+2005-02-20  Helge Hess  <helge.hess@opengroupware.org>
+
+       * AgenorUserManager.m: refactoring of the LDAP fetch code, added the
+         'SOGoFallbackIMAP4Server' default to configure the IMAP4 server when
+         LDAP is disabled (v0.9.31)
+
 2005-02-17  Helge Hess  <helge.hess@opengroupware.org>
 
        * moved in code from libSOGoLogic (unnecessarily a separate library)
index ddd701e921f1fdac7523e17a79bdd8f221dc8f41..b259838b860beb3160ee0d04896ce7d755b9ff7b 100644 (file)
@@ -1,5 +1,5 @@
 # version file
 
-SUBMINOR_VERSION:=30
+SUBMINOR_VERSION:=31
 
 # v0.9.26 requires libOGoContentStore v0.9.13