]> err.no Git - scalable-opengroupware.org/blob - SoObjects/SOGo/LDAPSource.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1277 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / SoObjects / SOGo / LDAPSource.m
1 /* LDAPSource.m - this file is part of SOGo
2  *
3  * Copyright (C) 2007 Inverse groupe conseil
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 #import <Foundation/NSArray.h>
24 #import <Foundation/NSDictionary.h>
25 #import <Foundation/NSString.h>
26
27 #import <EOControl/EOControl.h>
28 #import <NGLdap/NGLdapConnection.h>
29 #import <NGLdap/NGLdapAttribute.h>
30 #import <NGLdap/NGLdapEntry.h>
31
32 #import "LDAPSource.h"
33 #import "LDAPUserManager.h"
34
35 static NSArray *commonSearchFields;
36 static int timeLimit;
37 static int sizeLimit;
38
39 @implementation LDAPSource
40
41 + (void) initialize
42 {
43   NSUserDefaults *ud;
44
45   if (!commonSearchFields)
46     {
47       ud = [NSUserDefaults standardUserDefaults];
48       sizeLimit = [ud integerForKey: @"SOGoLDAPQueryLimit"];
49       timeLimit = [ud integerForKey: @"SOGoLDAPQueryTimeout"];
50
51       commonSearchFields = [NSArray arrayWithObjects:
52                                       @"title",
53                                     @"company",
54                                     @"o",
55                                     @"displayName",
56                                     @"modifytimestamp",
57                                     @"mozillaHomeState",
58                                     @"mozillaHomeUrl",
59                                     @"homeurl",
60                                     @"st",
61                                     @"region",
62                                     @"mozillaCustom2",
63                                     @"custom2",
64                                     @"mozillaHomeCountryName",
65                                     @"description",
66                                     @"notes",
67                                     @"department",
68                                     @"departmentnumber",
69                                     @"ou",
70                                     @"orgunit",
71                                     @"mobile",
72                                     @"cellphone",
73                                     @"carphone",
74                                     @"mozillaCustom1",
75                                     @"custom1",
76                                     @"mozillaNickname",
77                                     @"xmozillanickname",
78                                     @"mozillaWorkUrl",
79                                     @"workurl",
80                                     @"fax",
81                                     @"facsimileTelephoneNumber",
82                                     @"telephoneNumber",
83                                     @"mozillaHomeStreet",
84                                     @"mozillaSecondEmail",
85                                     @"xmozillasecondemail",
86                                     @"mozillaCustom4",
87                                     @"custom4",
88                                     @"nsAIMid",
89                                     @"nscpaimscreenname",
90                                     @"street",
91                                     @"streetAddress",
92                                     @"postOfficeBox",
93                                     @"homePhone",
94                                     @"cn",
95                                     @"commonname",
96                                     @"givenName",
97                                     @"mozillaHomePostalCode",
98                                     @"mozillaHomeLocalityName",
99                                     @"mozillaWorkStreet2",
100                                     @"mozillaUseHtmlMail",
101                                     @"xmozillausehtmlmail",
102                                     @"mozillaHomeStreet2",
103                                     @"postalCode",
104                                     @"zip",
105                                     @"c",
106                                     @"countryname",
107                                     @"pager",
108                                     @"pagerphone",
109                                     @"mail",
110                                     @"sn",
111                                     @"surname",
112                                     @"mozillaCustom3",
113                                     @"custom3",
114                                     @"l",
115                                     @"locality",
116                                     @"birthyear",
117                                     @"serialNumber",
118                                     @"calFBURL", @"proxyAddresses",
119                                     nil];
120         
121       [commonSearchFields retain];
122     }
123 }
124
125 + (id) sourceFromUDSource: (NSDictionary *) udSource
126 {
127   id newSource;
128
129   newSource = [[self alloc] initFromUDSource: udSource];
130   [newSource autorelease];
131
132   return newSource;
133 }
134
135 - (id) init
136 {
137   if ((self = [super init]))
138     {
139       bindDN = nil;
140       hostname = nil;
141       port = 389;
142       password = nil;
143       sourceID = nil;
144
145       baseDN = nil;
146       IDField = @"cn"; /* the first part of a user DN */
147       CNField = @"cn";
148       UIDField = @"uid";
149       mailFields = [NSArray arrayWithObject: @"mail"];
150       [mailFields retain];
151       bindFields = nil;
152
153       ldapConnection = nil;
154       searchAttributes = nil;
155     }
156
157   return self;
158 }
159
160 - (void) dealloc
161 {
162   [bindDN release];
163   [hostname release];
164   [password release];
165   [baseDN release];
166   [IDField release];
167   [CNField release];
168   [UIDField release];
169   [mailFields release];
170   [bindFields release];
171   [ldapConnection release];
172   [sourceID release];
173   [modulesConstraints release];
174   [super dealloc];
175 }
176
177 - (id) initFromUDSource: (NSDictionary *) udSource
178 {
179   self = [self init];
180
181   ASSIGN(sourceID, [udSource objectForKey: @"id"]);
182
183   [self setBindDN: [udSource objectForKey: @"bindDN"]
184         hostname: [udSource objectForKey: @"hostname"]
185         port: [udSource objectForKey: @"port"]
186         andPassword: [udSource objectForKey: @"bindPassword"]];
187   [self setBaseDN: [udSource objectForKey: @"baseDN"]
188         IDField: [udSource objectForKey: @"IDFieldName"]
189         CNField: [udSource objectForKey: @"CNFieldName"]
190         UIDField: [udSource objectForKey: @"UIDFieldName"]
191         mailFields: [udSource objectForKey: @"MailFieldNames"]
192         andBindFields: [udSource objectForKey: @"bindFields"]];
193   ASSIGN (modulesConstraints, [udSource objectForKey: @"ModulesConstraints"]);
194
195   return self;
196 }
197
198 - (void) setBindDN: (NSString *) newBindDN
199           hostname: (NSString *) newBindHostname
200               port: (NSString *) newBindPort
201        andPassword: (NSString *) newBindPassword
202 {
203   ASSIGN (bindDN, newBindDN);
204   ASSIGN (hostname, newBindHostname);
205   if (newBindPort)
206     port = [newBindPort intValue];
207   ASSIGN (password, newBindPassword);
208 }
209
210 - (void) setBaseDN: (NSString *) newBaseDN
211            IDField: (NSString *) newIDField
212            CNField: (NSString *) newCNField
213           UIDField: (NSString *) newUIDField
214         mailFields: (NSArray *) newMailFields
215      andBindFields: (NSString *) newBindFields
216 {
217   ASSIGN (baseDN, newBaseDN);
218   if (newIDField)
219     ASSIGN (IDField, newIDField);
220   if (CNField)
221     ASSIGN (CNField, newCNField);
222   if (UIDField)
223     ASSIGN (UIDField, newUIDField);
224   if (newMailFields)
225     ASSIGN (mailFields, newMailFields);
226   if (newBindFields)
227     ASSIGN (bindFields, newBindFields);
228 }
229
230 - (void) _initLDAPConnection
231 {
232   ldapConnection = [[NGLdapConnection alloc] initWithHostName: hostname
233                                              port: port];
234   [ldapConnection bindWithMethod: @"simple"
235                   binddn: bindDN
236                   credentials: password];
237   if (sizeLimit > 0)
238     [ldapConnection setQuerySizeLimit: sizeLimit];
239   if (timeLimit > 0)
240     [ldapConnection setQueryTimeLimit: timeLimit];
241 }
242
243 /* user management */
244 - (EOQualifier *) _qualifierForBindFilter: (NSString *) uid
245 {
246   NSMutableString *qs;
247   NSEnumerator *fields;
248   NSString *currentField;
249
250   qs = [NSMutableString string];
251   fields = [[bindFields componentsSeparatedByString: @","] objectEnumerator];
252   currentField = [fields nextObject];
253   while (currentField)
254     {
255       [qs appendFormat: @"OR (%@='%@')", currentField, uid];
256       currentField = [fields nextObject];
257     }
258   [qs deleteCharactersInRange: NSMakeRange (0, 3)];
259
260   return [EOQualifier qualifierWithQualifierFormat: qs];
261 }
262
263 - (NSString *) _fetchUserDNForLogin: (NSString *) loginToCheck
264 {
265   NSString *userDN;
266   NSEnumerator *entries;
267   NGLdapEntry *userEntry;
268
269   [self _initLDAPConnection];
270   entries = [ldapConnection deepSearchAtBaseDN: baseDN
271                             qualifier: [self _qualifierForBindFilter: loginToCheck]
272                             attributes: [NSArray arrayWithObject: @"dn"]];
273   userEntry = [entries nextObject];
274   if (userEntry)
275     userDN = [userEntry dn];
276   else
277     userDN = nil;
278   [ldapConnection release];
279
280   return userDN;
281 }
282
283 - (BOOL) checkLogin: (NSString *) loginToCheck
284         andPassword: (NSString *) passwordToCheck
285 {
286   BOOL didBind;
287   NSString *userDN;
288   NGLdapConnection *bindConnection;
289
290   didBind = NO;
291
292   if ([loginToCheck length] > 0)
293     {
294       bindConnection = [[NGLdapConnection alloc] initWithHostName: hostname
295                                                  port: port];
296       if (timeLimit > 0)
297         [ldapConnection setQueryTimeLimit: timeLimit];
298       if (bindFields)
299         userDN = [self _fetchUserDNForLogin: loginToCheck];
300       else
301         userDN = [NSString stringWithFormat: @"%@=%@,%@",
302                            IDField, loginToCheck, baseDN];
303       if (userDN)
304         {
305           NS_DURING
306             didBind = [bindConnection bindWithMethod: @"simple"
307                                       binddn: userDN
308                                       credentials: passwordToCheck];
309           NS_HANDLER
310           NS_ENDHANDLER
311         }
312       [bindConnection release];
313     }
314
315   return didBind;
316 }
317
318 /* contact management */
319 - (EOQualifier *) _qualifierForFilter: (NSString *) filter
320 {
321   NSString *qs;
322   EOQualifier *qualifier;
323
324   if ([filter length] > 0)
325     {
326       if ([filter isEqualToString: @"."])
327         qs = @"(cn='*')";
328       else
329         qs = [NSString stringWithFormat:
330                          @"(cn='%@*')"
331                        @"OR (sn='%@*')"
332                        @"OR (displayName='%@*')"
333                        @"OR (mail='%@*')"
334                        @"OR (telephoneNumber='*%@*')",
335                        filter, filter, filter, filter, filter];
336       qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
337     }
338   else
339     qualifier = nil;
340
341   return qualifier;
342 }
343
344 - (EOQualifier *) _qualifierForUIDFilter: (NSString *) uid
345 {
346   NSString *qs;
347
348   qs = [NSString stringWithFormat: (@"(%@='%@') OR (mail='%@')"
349                                     @" OR (mozillaSecondEmail='%@')"
350                                     @" OR (xmozillasecondemail='%@')"),
351                  UIDField, uid, uid, uid, uid];
352
353   return [EOQualifier qualifierWithQualifierFormat: qs];
354 }
355
356 - (NSArray *) _contraintsFields
357 {
358   NSMutableArray *fields;
359   NSEnumerator *values;
360   NSDictionary *currentConstraint;
361
362   fields = [NSMutableArray array];
363   values = [[modulesConstraints allValues] objectEnumerator];
364   while ((currentConstraint = [values nextObject]))
365     [fields addObjectsFromArray: [currentConstraint allKeys]];
366
367   return fields;
368 }
369
370 - (NSArray *) _searchAttributes
371 {
372   if (!searchAttributes)
373     {
374       searchAttributes = [NSMutableArray new];
375       if (CNField)
376         [searchAttributes addObject: CNField];
377       if (UIDField)
378         [searchAttributes addObject: UIDField];
379       [searchAttributes addObjectsFromArray: mailFields];
380       [searchAttributes addObjectsFromArray: [self _contraintsFields]];
381       [searchAttributes addObjectsFromArray: commonSearchFields];
382     }
383
384   return searchAttributes;
385 }
386
387 - (NSArray *) allEntryIDs
388 {
389   NSMutableArray *ids;
390   NSEnumerator *entries;
391   NGLdapEntry *currentEntry;
392   NSString *value;
393
394   ids = [NSMutableArray array];
395
396   [self _initLDAPConnection];
397   entries = [ldapConnection deepSearchAtBaseDN: baseDN
398                             qualifier: nil
399                             attributes: [NSArray arrayWithObject: IDField]];
400   if (entries)
401     {
402       currentEntry = [entries nextObject];
403       while (currentEntry)
404         {
405           value = [[currentEntry attributeWithName: IDField]
406                     stringValueAtIndex: 0];
407           if ([value length] > 0)
408             [ids addObject: value];
409           currentEntry = [entries nextObject];
410         }
411     }
412   [ldapConnection release];
413
414   return ids;
415 }
416
417 - (void) _fillEmailsOfEntry: (NGLdapEntry *) ldapEntry
418            intoContactEntry: (NSMutableDictionary *) contactEntry
419 {
420   NSEnumerator *emailFields;
421   NSString *currentFieldName, *value;
422   NSMutableArray *emails;
423
424   emails = [NSMutableArray new];
425   emailFields = [mailFields objectEnumerator];
426   while ((currentFieldName = [emailFields nextObject]))
427     {
428       value = [[ldapEntry attributeWithName: currentFieldName]
429                 stringValueAtIndex: 0];
430       if (value)
431         [emails addObject: value];
432     }
433   [emails autorelease];
434   [contactEntry setObject: emails forKey: @"c_emails"];
435 }
436
437 - (void) _fillConstraints: (NGLdapEntry *) ldapEntry
438                 forModule: (NSString *) module
439          intoContactEntry: (NSMutableDictionary *) contactEntry
440 {
441   NSDictionary *constraints;
442   NSEnumerator *matches;
443   NSString *currentMatch, *currentValue, *ldapValue;
444   BOOL result;
445
446   result = YES;
447
448   constraints = [modulesConstraints objectForKey: module];
449   if (constraints)
450     {
451       matches = [[constraints allKeys] objectEnumerator];
452       currentMatch = [matches nextObject];
453       while (result && currentMatch)
454         {
455           ldapValue = [[ldapEntry attributeWithName: currentMatch]
456                         stringValueAtIndex: 0];
457           currentValue = [constraints objectForKey: currentMatch];
458           if ([ldapValue isEqualToString: currentValue])
459             currentMatch = [matches nextObject];
460           else
461             result = NO;
462         }
463     }
464
465   [contactEntry setObject: [NSNumber numberWithBool: result]
466                 forKey: [NSString stringWithFormat: @"%@Access", module]];
467 }
468
469 - (NSDictionary *) _convertLDAPEntryToContact: (NGLdapEntry *) ldapEntry
470 {
471   NSMutableDictionary *contactEntry;
472   NSEnumerator *attributes;
473   NSString *currentAttribute, *value;
474
475   contactEntry = [NSMutableDictionary dictionary];
476   attributes = [[self _searchAttributes] objectEnumerator];
477   currentAttribute = [attributes nextObject];
478   while (currentAttribute)
479     {
480       value = [[ldapEntry attributeWithName: currentAttribute]
481                 stringValueAtIndex: 0];
482       if (value)
483         [contactEntry setObject: value forKey: currentAttribute];
484       currentAttribute = [attributes nextObject];
485     }
486   value = [[ldapEntry attributeWithName: IDField] stringValueAtIndex: 0];
487   if (!value)
488     value = @"";
489   [contactEntry setObject: value forKey: @"c_name"];
490   value = [[ldapEntry attributeWithName: UIDField] stringValueAtIndex: 0];
491   if (!value)
492     value = @"";
493   [contactEntry setObject: value forKey: @"c_uid"];
494   value = [[ldapEntry attributeWithName: CNField] stringValueAtIndex: 0];
495   if (!value)
496     value = @"";
497   [contactEntry setObject: value forKey: @"c_cn"];
498   [self _fillEmailsOfEntry: ldapEntry intoContactEntry: contactEntry];
499   [self _fillConstraints: ldapEntry forModule: @"Calendar"
500         intoContactEntry: (NSMutableDictionary *) contactEntry];
501   [self _fillConstraints: ldapEntry forModule: @"Mail"
502         intoContactEntry: (NSMutableDictionary *) contactEntry];
503
504   return contactEntry;
505 }
506
507 - (NSArray *) fetchContactsMatching: (NSString *) match
508 {
509   NSMutableArray *contacts;
510   NGLdapEntry *currentEntry;
511   NSEnumerator *entries;
512
513   contacts = [NSMutableArray array];
514
515   if ([match length] > 0)
516     {
517       [self _initLDAPConnection];
518       entries = [ldapConnection deepSearchAtBaseDN: baseDN
519                                 qualifier: [self _qualifierForFilter: match]
520                                 attributes: [self _searchAttributes]];
521       if (entries)
522         {
523           currentEntry = [entries nextObject];
524           while (currentEntry)
525             {
526               [contacts addObject:
527                           [self _convertLDAPEntryToContact: currentEntry]];
528               currentEntry = [entries nextObject];
529             }
530         }
531       [ldapConnection release];
532     }
533
534   return contacts;
535 }
536
537 - (NSDictionary *) lookupContactEntry: (NSString *) entryID;
538 {
539   NSDictionary *contactEntry;
540   NGLdapEntry *ldapEntry;
541
542   contactEntry = nil;
543
544   if ([entryID length] > 0)
545     {
546       [self _initLDAPConnection];
547       ldapEntry
548         = [ldapConnection entryAtDN: [NSString stringWithFormat: @"%@=%@,%@",
549                                                IDField, entryID, baseDN]
550                           attributes: [self _searchAttributes]];
551       if (ldapEntry)
552         contactEntry = [self _convertLDAPEntryToContact: ldapEntry];
553       [ldapConnection release];
554     }
555
556   return contactEntry;
557 }
558
559 - (NSDictionary *) lookupContactEntryWithUIDorEmail: (NSString *) uid;
560 {
561   NSDictionary *contactEntry;
562   NGLdapEntry *ldapEntry;
563   NSEnumerator *entries;
564   EOQualifier *qualifier;
565
566   contactEntry = nil;
567
568   if ([uid length] > 0)
569     {
570       [self _initLDAPConnection];
571       qualifier = [self _qualifierForUIDFilter: uid];
572       entries = [ldapConnection deepSearchAtBaseDN: baseDN
573                                 qualifier: qualifier
574                                 attributes: [self _searchAttributes]];
575       ldapEntry = [entries nextObject];
576       if (ldapEntry)
577         contactEntry = [self _convertLDAPEntryToContact: ldapEntry];
578       [ldapConnection release];
579     }
580
581   return contactEntry;
582 }
583
584 - (NSString *) sourceID
585 {
586   return sourceID;
587 }
588
589 @end