1/* SOGoLDAPUserDefaults.m - this file is part of SOGo 2 * 3 * Copyright (C) 2008 Inverse inc. 4 * 5 * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> 6 * 7 * This file is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2, or (at your option) 10 * any later version. 11 * 12 * This file is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; see the file COPYING. If not, write to 19 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 * Boston, MA 02111-1307, USA. 21 */ 22 23#include <stdio.h> 24#include <stdlib.h> 25#include <ldap.h> 26 27#import <Foundation/NSArray.h> 28#import <Foundation/NSDictionary.h> 29#import <Foundation/NSString.h> 30 31#import <NGExtensions/NSObject+Logs.h> 32 33#import "SOGoLDAPUserDefaults.h" 34 35#define SOGoLDAPDescriptor @"/usr/local/etc/sogo.conf" 36#define SOGoLDAPContainerSize 8192 37#define SOGoLDAPAttrRefSeparator '/' 38 39typedef enum _SOGoLDAPValueType { 40 SOGoLDAPAtom, 41 SOGoLDAPArray, 42 SOGoLDAPDictionary, 43 SOGoLDAPLastType 44} _SOGoLDAPValueType; 45 46typedef struct _SOGoLDAPValue { 47 _SOGoLDAPValueType type; 48 void *value; 49 unsigned int maxCount; 50 char *key; 51} _SOGoLDAPValue; 52 53@implementation SOGoLDAPUserDefaults 54 55static _SOGoLDAPValue * 56_createAtom (_SOGoLDAPValueType type, void *value) 57{ 58 _SOGoLDAPValue *newAtom; 59 60 newAtom = NSZoneMalloc (NULL, sizeof (_SOGoLDAPValue)); 61 newAtom->type = type; 62 newAtom->value = value; 63 newAtom->maxCount = 0; 64 newAtom->key = NULL; 65 66 return newAtom; 67} 68 69static _SOGoLDAPValue * 70_createContainer (_SOGoLDAPValueType type) 71{ 72 _SOGoLDAPValue *newContainer; 73 _SOGoLDAPValue **array; 74 75 array = NSZoneMalloc (NULL, 76 sizeof (_SOGoLDAPValue *) * SOGoLDAPContainerSize); 77 *array = NULL; 78 newContainer = _createAtom (type, array); 79 newContainer->maxCount = SOGoLDAPContainerSize - 1; /* all values + NULL */ 80 81 return newContainer; 82} 83 84static void 85_appendAtomToContainer (_SOGoLDAPValue *atom, _SOGoLDAPValue *container) 86{ 87 unsigned int count; 88 _SOGoLDAPValue **atoms, **currentAtom; 89 90 atoms = (_SOGoLDAPValue **) container->value; 91 currentAtom = atoms; 92 while (*currentAtom) 93 currentAtom++; 94 95 count = (currentAtom - atoms); 96 if (count >= container->maxCount) 97 { 98 container->maxCount += SOGoLDAPContainerSize; 99 container->value = NSZoneRealloc (NULL, container->value, 100 (container->maxCount + 1) 101 * sizeof (_SOGoLDAPValue *)); 102 currentAtom = (_SOGoLDAPValue **) container->value + count; 103 } 104 105 *currentAtom = atom; 106 *(currentAtom + 1) = NULL; 107} 108 109static _SOGoLDAPValue ** 110_findAtomInDictionary (const char *key, const _SOGoLDAPValue *dictionary) 111{ 112 _SOGoLDAPValue **atom, **value; 113 114 atom = NULL; 115 116 value = dictionary->value; 117 if (value) 118 { 119 while (!atom && *value) 120 if (strcmp ((*value)->key, key) == 0) 121 atom = value; 122 else 123 value++; 124 } 125 126 return atom; 127} 128 129static void 130_appendAtomToDictionary (_SOGoLDAPValue *atom, _SOGoLDAPValue *dictionary) 131{ 132 _SOGoLDAPValue **oldAtomPtr, *oldAtom, *container; 133 134 oldAtomPtr = _findAtomInDictionary (atom->key, dictionary); 135 if (oldAtomPtr) 136 { 137 oldAtom = *oldAtomPtr; 138 if (oldAtom->type == SOGoLDAPArray) 139 container = oldAtom; 140 else 141 { 142 container = _createContainer (SOGoLDAPArray); 143 container->key = oldAtom->key; 144 oldAtom->key = NULL; 145 _appendAtomToContainer (oldAtom, container); 146 *oldAtomPtr = container; 147 } 148 } 149 else 150 container = dictionary; 151 152 if (container->type == SOGoLDAPArray) 153 { 154 NSZoneFree (NULL, atom->key); 155 atom->key = NULL; 156 } 157 _appendAtomToContainer (atom, container); 158} 159 160static _SOGoLDAPValue *_readLDAPAttrRefWithHandle (const char *attrPair, 161 LDAP *ldapHandle); 162 163static _SOGoLDAPValue ** 164_fetchLDAPDNAndAttrWithHandle (const char *dn, char *attrs[], LDAP *ldapHandle) 165{ 166 struct timeval timeout; 167 int rc; 168 LDAPMessage *messages, *message; 169 BerElement *element; 170 BerValue **values, **value; 171 const char *attribute; 172 _SOGoLDAPValue **atoms, **currentAtom; 173 unsigned int atomsCount; 174 175 atoms = NSZoneMalloc (NULL, 176 SOGoLDAPContainerSize * sizeof (_SOGoLDAPValue *)); 177 currentAtom = atoms; 178 atomsCount = 0; 179 180 timeout.tv_sec = 30; 181 timeout.tv_usec = 0; 182 183 rc = ldap_search_ext_s (ldapHandle, dn, LDAP_SCOPE_BASE, "(objectClass=*)", 184 attrs, 0, NULL, NULL, &timeout, 0, &messages); 185 if (rc == LDAP_SUCCESS) 186 { 187 message = ldap_first_entry (ldapHandle, messages); 188 if (message) 189 { 190 attribute = ldap_first_attribute (ldapHandle, message, &element); 191 while (attribute) 192 { 193 values = ldap_get_values_len (ldapHandle, message, attribute); 194 value = values; 195 while (*value) 196 { 197 if (strncmp ((*value)->bv_val, "ref-dn:", 7) == 0) 198 *currentAtom 199 = _readLDAPAttrRefWithHandle (((*value)->bv_val + 7), 200 ldapHandle); 201 else 202 *currentAtom = _createAtom (SOGoLDAPAtom, 203 strdup ((*value)->bv_val)); 204 (*currentAtom)->key = strdup (attribute); 205 currentAtom++; 206 atomsCount++; 207 208 if (!(atomsCount % SOGoLDAPContainerSize)) 209 { 210 atoms = NSZoneRealloc (NULL, atoms, 211 (int) (SOGoLDAPContainerSize 212 + atomsCount) 213 * sizeof (_SOGoLDAPValue *)); 214 currentAtom = atoms + atomsCount; 215 } 216 value++; 217 } 218 ldap_value_free_len (values); 219 attribute = ldap_next_attribute (ldapHandle, message, element); 220 } 221 } 222 ldap_msgfree (message); 223 } 224 225 *currentAtom = NULL; 226 227 return atoms; 228} 229 230static void 231_fillLDAPDictionaryWithAtoms (_SOGoLDAPValue *container, 232 _SOGoLDAPValue *atoms[]) 233{ 234 _SOGoLDAPValue **currentAtom; 235 236 currentAtom = atoms; 237 while (*currentAtom) 238 { 239 _appendAtomToDictionary (*currentAtom, container); 240 currentAtom++; 241 } 242} 243 244static _SOGoLDAPValue * 245_readLDAPAttrRefWithHandle (const char *attrPair, LDAP *ldapHandle) 246{ 247 char *dn, *separator; 248 _SOGoLDAPValue *refValue; 249 _SOGoLDAPValue **atoms; 250 char *attrs[2]; 251 252 dn = strdup (attrPair); 253 separator = strrchr (dn, SOGoLDAPAttrRefSeparator); 254 if (separator) 255 { 256 *separator = 0; 257 attrs[0] = strdup (separator + 1); 258 attrs[1] = NULL; 259 atoms = _fetchLDAPDNAndAttrWithHandle (dn, attrs, ldapHandle); 260 refValue = *atoms; 261 NSZoneFree (NULL, attrs[0]); 262 } 263 else 264 { 265 refValue = _createContainer (SOGoLDAPDictionary); 266 atoms = _fetchLDAPDNAndAttrWithHandle (dn, NULL, ldapHandle); 267 _fillLDAPDictionaryWithAtoms (refValue, atoms); 268 } 269 NSZoneFree (NULL, atoms); 270 NSZoneFree (NULL, dn); 271 272 return refValue; 273} 274 275static NSString *_convertLDAPAtomToNSString (_SOGoLDAPValue *atom); 276static NSArray *_convertLDAPAtomToNSArray (_SOGoLDAPValue *atom); 277static NSDictionary *_convertLDAPAtomToNSDictionary (_SOGoLDAPValue *atom); 278 279static id 280_convertLDAPAtomToNSObject (_SOGoLDAPValue *atom) 281{ 282 id ldapObject; 283 284 if (atom->type == SOGoLDAPAtom) 285 ldapObject = _convertLDAPAtomToNSString (atom); 286 else if (atom->type == SOGoLDAPArray) 287 ldapObject = _convertLDAPAtomToNSArray (atom); 288 else 289 ldapObject = _convertLDAPAtomToNSDictionary (atom); 290 291 return ldapObject; 292} 293 294static NSString * 295_convertLDAPAtomToNSString (_SOGoLDAPValue *atom) 296{ 297 NSString *ldapObject; 298 299 ldapObject = [[NSString alloc] 300 initWithBytes: atom->value 301 length: strlen (atom->value) 302 encoding: NSUTF8StringEncoding]; 303 [ldapObject autorelease]; 304 305 return ldapObject; 306} 307 308static NSArray * 309_convertLDAPAtomToNSArray (_SOGoLDAPValue *atom) 310{ 311 _SOGoLDAPValue **currentSubAtom; 312 NSMutableArray *ldapObject; 313 314 ldapObject = [NSMutableArray array]; 315 316 currentSubAtom = atom->value; 317 while (*currentSubAtom) 318 { 319 [ldapObject addObject: _convertLDAPAtomToNSObject (*currentSubAtom)]; 320 currentSubAtom++; 321 } 322 323 return ldapObject; 324} 325 326static NSDictionary * 327_convertLDAPAtomToNSDictionary (_SOGoLDAPValue *atom) 328{ 329 _SOGoLDAPValue **currentSubAtom; 330 NSMutableDictionary *ldapObject; 331 NSString *atomKey; 332 333 ldapObject = [NSMutableDictionary dictionary]; 334 335 currentSubAtom = atom->value; 336 while (*currentSubAtom) 337 { 338 atomKey = [[NSString alloc] 339 initWithBytes: (*currentSubAtom)->key 340 length: strlen ((*currentSubAtom)->key) 341 encoding: NSUTF8StringEncoding]; 342 [atomKey autorelease]; 343 [ldapObject setObject: _convertLDAPAtomToNSObject (*currentSubAtom) 344 forKey: atomKey]; 345 currentSubAtom++; 346 } 347 348 return ldapObject; 349} 350 351// dn = "cn=admin,dc=inverse,dc=ca"; 352// password = "qwerty"; 353// uri = "ldap://127.0.0.1"; 354// configDN = "cn=sogo-config,dc=inverse,dc=ca"; 355 356static _SOGoLDAPValue * 357_initLDAPDefaults () 358{ 359#warning dn, password, uri, configDN should be defined 360 const char *dn, *password, *uri, *configDN; 361 LDAP *ldapHandle; 362 int rc, opt; 363 _SOGoLDAPValue *dictionary; 364 _SOGoLDAPValue **atoms; 365 BerValue cred; 366 367 ldap_initialize (&ldapHandle, uri); 368 369 opt = LDAP_VERSION3; 370 rc = ldap_set_option (ldapHandle, LDAP_OPT_PROTOCOL_VERSION, &opt); 371 rc = ldap_set_option (ldapHandle, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 372 373 dictionary = _createContainer (SOGoLDAPDictionary); 374 375 cred.bv_val = (char *) password; 376 cred.bv_len = strlen (password); 377 378 rc = ldap_sasl_bind_s (ldapHandle, dn, LDAP_SASL_SIMPLE, &cred, 379 NULL, NULL, NULL); 380 if (rc == LDAP_SUCCESS) 381 { 382 atoms = _fetchLDAPDNAndAttrWithHandle (configDN, NULL, ldapHandle); 383 _fillLDAPDictionaryWithAtoms (dictionary, atoms); 384 NSZoneFree (NULL, atoms); 385 } 386 387 return dictionary; 388} 389 390- (id) objectForKey: (NSString *) key 391{ 392 static _SOGoLDAPValue *SOGoLDAPDefaults = NULL; 393 _SOGoLDAPValue **atom; 394 id ldapObject; 395 396 if (!SOGoLDAPDefaults) 397 SOGoLDAPDefaults = _initLDAPDefaults (); 398 399 atom = _findAtomInDictionary ([key UTF8String], SOGoLDAPDefaults); 400 ldapObject = ((atom) 401 ? _convertLDAPAtomToNSObject (*atom) 402 : NULL); 403 404 if (!ldapObject) 405 ldapObject = [super objectForKey: key]; 406 407 return ldapObject; 408} 409 410@end 411