]> err.no Git - scalable-opengroupware.org/blob - SOGo/SoObjects/SOGo/AgenorUserManager.m
implemented homepage (almost done), removed 'schedule' tab
[scalable-opengroupware.org] / SOGo / SoObjects / SOGo / AgenorUserManager.m
1 /*
2   Copyright (C) 2004-2005 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
6   OGo is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "AgenorUserManager.h"
23 #include "AgenorUserDefaults.h"
24 #include <NGExtensions/NGExtensions.h>
25 #include <NGLdap/NGLdap.h>
26 #include <NGiCal/NGiCal.h>
27 #include "SOGoLRUCache.h"
28
29 @interface AgenorUserManager (PrivateAPI)
30 - (NGLdapConnection *)ldapConnection;
31
32 - (void)_cacheCN:(NSString *)_cn forUID:(NSString *)_uid;
33 - (NSString *)_cachedCNForUID:(NSString *)_uid;
34 - (void)_cacheServer:(NSString *)_server forUID:(NSString *)_uid;
35 - (NSString *)_cachedServerForUID:(NSString *)_uid;
36 - (void)_cacheEmail:(NSString *)_email forUID:(NSString *)_uid;
37 - (NSString *)_cachedEmailForUID:(NSString *)_uid;
38 - (void)_cacheUID:(NSString *)_uid forEmail:(NSString *)_email;
39 - (NSString *)_cachedUIDForEmail:(NSString *)_email;
40
41 - (BOOL)primaryIsUserAllowedToChangeSOGoInternetAccess:(NSString *)_uid;
42 @end
43
44 // TODO: add a timer to flush LRU caches every some hours
45
46 @implementation AgenorUserManager
47
48 static BOOL     debugOn     = NO;
49 static BOOL     useLDAP     = NO;
50 static NSString *ldapHost   = nil;
51 static NSString *ldapBaseDN = nil;
52 static NSNull   *sharedNull = nil;
53 static NSString *fallbackIMAP4Server          = nil;
54 static NSString *defaultMailDomain            = @"equipement.gouv.fr";
55 static NSString *shareLDAPClass               = @"mineqMelBoite";
56 static NSString *shareLoginSeparator          = @".-.";
57 static NSString *mailEmissionAttrName         = @"mineqMelmailEmission";
58 static NSString *changeInternetAccessAttrName = @"mineqOgoAccesInternet";
59 static NSURL    *AgenorProfileURL             = nil;
60
61 static NSArray *fromEMailAttrs = nil;
62
63 + (void)initialize {
64   static BOOL didInit = NO;
65   NSUserDefaults *ud;
66   NSString *tmp;
67
68   if (didInit) return;
69   didInit = YES;
70   
71   ud      = [NSUserDefaults standardUserDefaults];
72   debugOn = [ud boolForKey:@"SOGoUserManagerDebugEnabled"];
73   
74   useLDAP = [ud boolForKey:@"SOGoUserManagerUsesLDAP"];
75   if (useLDAP) {
76     ldapHost   = [[ud stringForKey:@"SOGoLDAPHost"]   copy];
77     ldapBaseDN = [[ud stringForKey:@"SOGoLDAPBaseDN"] copy];
78     NSLog(@"Note: using LDAP host to manage accounts: %@", ldapHost);
79   }
80   else
81     NSLog(@"Note: LDAP access is disabled.");
82   
83   fallbackIMAP4Server = [[ud stringForKey:@"SOGoFallbackIMAP4Server"] copy];
84   if ([fallbackIMAP4Server length] > 0)
85     NSLog(@"Note: using fallback IMAP4 server: '%@'", fallbackIMAP4Server);
86   else
87     fallbackIMAP4Server = nil;
88
89   fromEMailAttrs = 
90     [[NSArray alloc] initWithObjects:mailEmissionAttrName, nil];
91
92   sharedNull = [[NSNull null] retain];
93
94   /* profile database URL */
95   
96   if ((tmp = [ud stringForKey:@"AgenorProfileURL"]) == nil)
97     NSLog(@"ERROR: no 'AgenorProfileURL' database URL configured!");
98   else if ((AgenorProfileURL = [[NSURL alloc] initWithString:tmp]) == nil)
99     NSLog(@"ERROR: could not parse AgenorProfileURL: '%@'", tmp);
100   else
101     NSLog(@"Note: using profile at: %@", [AgenorProfileURL absoluteString]);
102 }
103
104 + (id)sharedUserManager {
105   static AgenorUserManager *mgr = nil;
106   if (mgr == nil)
107     mgr = [[self alloc] init];
108   return mgr;
109 }
110
111 - (id)init {
112   self = [super init];
113   if(self) {
114     self->serverCache     = [[SOGoLRUCache alloc] initWithCacheSize:10000];
115     self->cnCache         = [[SOGoLRUCache alloc] initWithCacheSize:10000];
116     self->uidCache        = [[SOGoLRUCache alloc] initWithCacheSize:10000];
117     self->emailCache      = [[SOGoLRUCache alloc] initWithCacheSize:10000];
118     self->shareStoreCache = [[SOGoLRUCache alloc] initWithCacheSize:10000];
119     self->shareEMailCache = [[SOGoLRUCache alloc] initWithCacheSize:10000];
120     self->changeInternetAccessCache =
121       [[SOGoLRUCache alloc] initWithCacheSize:10000];
122   }
123   return self;
124 }
125
126 - (void)dealloc {
127   [self->serverCache               release];
128   [self->cnCache                   release];
129   [self->uidCache                  release];
130   [self->emailCache                release];
131   [self->shareStoreCache           release];
132   [self->shareEMailCache           release];
133   [self->changeInternetAccessCache release];
134   [super dealloc];
135 }
136
137 - (NGLdapConnection *)ldapConnection {
138   static NGLdapConnection *ldapConnection = nil;
139   if(!ldapConnection) {
140     ldapConnection = [[NGLdapConnection alloc] initWithHostName:ldapHost];
141 #if 0
142     [ldapConnection setUseCache:YES];
143 #endif
144   }
145   return ldapConnection;
146 }
147
148
149 /* private cache helpers */
150 // TODO: this is really unnecessary, no?
151
152 - (void)_cacheCN:(NSString *)_cn forUID:(NSString *)_uid {
153   if (_cn == nil) return;
154   [self->cnCache addObject:_cn forKey:_uid];
155 }
156 - (NSString *)_cachedCNForUID:(NSString *)_uid {
157   return [self->cnCache objectForKey:_uid];
158 }
159
160 - (void)_cacheServer:(NSString *)_server forUID:(NSString *)_uid {
161   if (_server == nil) return;
162   [self->serverCache addObject:_server forKey:_uid];
163 }
164 - (NSString *)_cachedServerForUID:(NSString *)_uid {
165   return [self->serverCache objectForKey:_uid];
166 }
167
168 - (void)_cacheEmail:(NSString *)_email forUID:(NSString *)_uid {
169   if (_email == nil) return;
170   [self->emailCache addObject:_email forKey:_uid];
171 }
172 - (NSString *)_cachedEmailForUID:(NSString *)_uid {
173   return [self->emailCache objectForKey:_uid];
174 }
175
176 - (void)_cacheUID:(NSString *)_uid forEmail:(NSString *)_email {
177   if (_uid == nil) return;
178   [self->uidCache addObject:_uid forKey:_email];
179 }
180 - (NSString *)_cachedUIDForEmail:(NSString *)_email {
181   return [self->uidCache objectForKey:_email];
182 }
183
184
185 /* uid <-> email mapping */
186
187 /*
188  UPDATE: the email excerpt below has been marked by Maxime as being
189          wrong. This algorithm can not be expected to work, thus
190          the mapping has been replaced with an LDAP query.
191
192  --- snip ---
193  The uid field is in bijection this the email adress :
194  this field can be construct from the email. Email are uniques.
195  
196  So, we can use email adresse from identifier. 
197  The field is made like this :
198  _ if the email is equipement.gouv.fr then the login
199  is the part before the @
200  for example : fisrtName.lastName
201  _ if the email is not equipement.gouv.fr then the login
202  is the full email adress where @ is change to . (dot)
203  for example : fisrtName.lastName.subDomain.domain.tld
204  --- snap ---
205
206  NOTE: mapping email -> uid is easy, but can also generate uid's not known
207        to the system (i.e. for private addressbook entries, obvious).
208        The reverse mapping can work _only_ if "firstName.lastname." is
209        guaranteed, because the second dot would be mapped to '@'. This
210        is probably error prone.
211        Only LDAP fetches would guarantee correctness in both cases.
212 */
213
214 - (NSString *)primaryGetAgenorUIDForEmail:(NSString *)_email {
215   static NSArray   *uidAttrs = nil;
216   NGLdapConnection *conn;
217   EOQualifier      *q;
218   NSEnumerator     *resultEnum;
219   NGLdapEntry      *entry;
220   NGLdapAttribute  *uidAttr;
221   NSString         *uid;
222
223   if (uidAttrs == nil)
224     uidAttrs = [[NSArray alloc] initWithObjects:@"uid", nil];
225     
226   q = [EOQualifier qualifierWithQualifierFormat:@"mail = %@", _email];
227     
228   conn       = [self ldapConnection];
229   resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN
230                      qualifier:q
231                      attributes:uidAttrs];
232   entry = [resultEnum nextObject];
233   if (entry == nil) {
234     if(debugOn) {
235       [self logWithFormat:@"%s Didn't find LDAP entry for email '%@'!",
236             __PRETTY_FUNCTION__,
237             _email];
238     }
239     return nil;
240   }
241   uidAttr = [entry attributeWithName:@"uid"];
242   if (!uidAttr)
243     return nil; /* can happen, not unlikely */
244   uid = [uidAttr stringValueAtIndex:0];
245   return uid;
246 }
247
248 - (NSString *)getUIDForEmail:(NSString *)_email {
249   NSString *uid;
250   
251   if ((uid = [self _cachedUIDForEmail:_email]) != nil)
252     return uid;
253     
254   if (useLDAP) {
255     uid = [self primaryGetAgenorUIDForEmail:_email];
256   }
257   else {
258     NSRange r;
259     NSString *domain;
260     
261     if(!_email || [_email length] == 0)
262       return nil;
263     
264     r = [_email rangeOfString:@"@"];
265     if (r.length == 0)
266       return nil;
267     
268     domain = [_email substringFromIndex:NSMaxRange(r)];
269     if (![domain isEqualToString:defaultMailDomain])
270       uid = _email;
271     else
272       uid = [_email substringToIndex:r.location];
273   }
274   
275   [self _cacheUID:uid forEmail:_email];
276   return uid;
277 }
278
279 - (NSString *)getUIDForICalPerson:(iCalPerson *)_person {
280   return [self getUIDForEmail:[_person rfc822Email]];
281 }
282
283   /* may insert NSNulls into returned array */
284 - (NSArray  *)getUIDsForICalPersons:(NSArray *)_persons
285   applyStrictMapping:(BOOL)_mapStrictly
286 {
287   NSMutableArray *ma;
288   unsigned       i, count;
289
290   count = [_persons count];
291   ma    = [[[NSMutableArray alloc] initWithCapacity:count] autorelease];
292   for (i = 0; i < count; i++) {
293     iCalPerson *p;
294     id         uid;
295
296     p   = [_persons objectAtIndex:i];
297     uid = [self getUIDForICalPerson:p];
298     if (uid) {
299       [ma addObject:uid];
300     }
301     else if (!uid && _mapStrictly) {
302       [ma addObject:sharedNull];
303     }
304   }
305   return ma;
306 }
307
308 - (NSString *)primaryGetEmailForAgenorUID:(NSString *)_uid {
309   NGLdapConnection *conn;
310   EOQualifier      *q;
311   NSEnumerator     *resultEnum;
312   NGLdapEntry      *entry;
313   NGLdapAttribute  *emailAttr;
314   NSString         *email;
315   unsigned         count;
316     
317   q = [EOQualifier qualifierWithQualifierFormat:@"uid = %@", _uid];
318     
319   conn       = [self ldapConnection];
320   resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN
321                      qualifier:q
322                      attributes:fromEMailAttrs];
323   entry = [resultEnum nextObject];
324   if (entry == nil) {
325       if(debugOn) {
326         [self logWithFormat:@"%s Didn't find LDAP entry for uid '%@'!",
327                               __PRETTY_FUNCTION__,
328                               _uid];
329       }
330       return nil;
331   }
332   emailAttr = [entry attributeWithName:mailEmissionAttrName];
333   if (emailAttr == nil)
334     return nil; /* shit happens */
335   
336   email = nil;
337   count = [emailAttr count];
338 #if 0 // TODO: explain why this is commented out!
339   if (count > 1) {
340         unsigned i;
341
342         /* in case there are multiple email addresses, select the first
343            which doesn't have '@equipement.gouv.fr' in it */
344         for (i = 0; i < count; i++) {
345           NSString *candidate;
346           
347           candidate = [emailAttr stringValueAtIndex:i];
348           if (![candidate hasSuffix:defaultMailDomain]) {
349             // TODO: also check for '@'
350             email = candidate;
351             break;
352           }
353         }
354   }
355 #endif
356   if (email == nil && count > 0)
357     email = [emailAttr stringValueAtIndex:0];
358   
359   return email;
360 }
361
362 - (NSString *)getEmailForUID:(NSString *)_uid {
363   NSString *email;
364   
365   if (![_uid isNotNull] || [_uid length] == 0)
366     return nil;
367   if ((email = [self _cachedEmailForUID:_uid]) != nil)
368     return email;
369   
370   if (useLDAP) {
371     email = [self primaryGetEmailForAgenorUID:_uid];
372   }
373   else {
374     NSRange r;
375     
376     r = [_uid rangeOfString:@"@"];
377     email = (r.length > 0)
378       ? _uid
379       : [[_uid stringByAppendingString:@"@"]
380                stringByAppendingString:defaultMailDomain];
381   }
382   
383   [self _cacheEmail:email forUID:_uid];
384   return email;
385 }
386
387
388 /* CN */
389
390 - (NSString *)primaryGetCNForAgenorUID:(NSString *)_uid {
391   static NSArray   *cnAttrs = nil;
392   NGLdapConnection *conn;
393   EOQualifier      *q;
394   NSEnumerator     *resultEnum;
395   NGLdapEntry      *entry;
396   NGLdapAttribute  *cnAttr;
397   NSString         *cn;
398
399   if (cnAttrs == nil)
400     cnAttrs = [[NSArray alloc] initWithObjects:@"cn", nil];
401   
402   q = [EOQualifier qualifierWithQualifierFormat:@"uid = %@", _uid];
403   
404   conn = [self ldapConnection];
405   resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN
406                      qualifier:q
407                      attributes:cnAttrs];
408   entry = [resultEnum nextObject];
409   if (entry == nil) {
410       if(debugOn) {
411         [self logWithFormat:@"%s Didn't find LDAP entry for uid '%@'!",
412                               __PRETTY_FUNCTION__,
413                               _uid];
414       }
415       return nil;
416   }
417   cnAttr = [entry attributeWithName:@"cn"];
418   if(cnAttr == nil && debugOn) {
419       [self logWithFormat:@"%s LDAP entry for uid '%@' has no common name?",
420                             __PRETTY_FUNCTION__,
421                             _uid];
422       return nil; /* nothing we can do about it */
423   }
424   cn = [cnAttr stringValueAtIndex:0];
425   return cn;
426 }
427
428 - (NSString *)getCNForUID:(NSString *)_uid {
429   NSString *cn;
430
431   if ((cn = [self _cachedCNForUID:_uid]) != nil)
432     return cn;
433   
434   if (useLDAP) {
435     cn = [self primaryGetCNForAgenorUID:_uid];
436   }
437   else {
438     NSString *s;
439     NSRange  r;
440     
441     s = _uid;
442     if ([s length] < 10)
443       return s;
444     
445     // TODO: algorithm might be inappropriate, depends on the actual UID
446     r = [s rangeOfString:@"."];
447     if (r.length == 0)
448       cn = s;
449     else
450       cn = [s substringToIndex:r.location];
451   }
452   
453   [self _cacheCN:cn forUID:_uid];
454   return cn;
455 }
456
457
458 /* Servers, IMAP */
459
460 - (NSString *)getIMAPAccountStringForUID:(NSString *)_uid {
461   NSString *server;
462   
463   server = [self getServerForUID:_uid];
464   if (server == nil)
465     return nil;
466   return [NSString stringWithFormat:@"%@@%@", _uid, server];
467 }
468
469 - (NSArray *)mailServerDiscoveryAttributes {
470   static NSArray *attrs = nil;
471
472   if (attrs == nil) {
473     attrs = [[NSArray alloc] initWithObjects:
474                                @"uid", /* required for shares */
475                                @"mineqMelRoutage", 
476                                @"mineqMelServeurPrincipal",
477                              nil];
478   }
479   return attrs;
480 }
481
482 - (NGLdapEntry *)_fetchEntryForAgenorUID:(NSString *)_uid {
483   // TODO: badly named, this fetches the mail server discovery attributes
484   /* called by -primaryGetServerForAgenorUID: */
485   NGLdapConnection *conn;
486   EOQualifier      *q;
487   NSEnumerator     *resultEnum;
488   NGLdapEntry      *entry;
489   
490   q = [EOQualifier qualifierWithQualifierFormat:@"uid = %@", _uid];
491   
492   conn = [self ldapConnection];
493   resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN
494                      qualifier:q
495                      attributes:[self mailServerDiscoveryAttributes]];
496   /* we just expect one entry, thus drop the rest */
497   entry = [resultEnum nextObject];
498   if (entry == nil) {
499       if(debugOn) {
500         NSLog(@"%s Didn't find LDAP entry for uid '%@'!",
501               __PRETTY_FUNCTION__,
502               _uid);
503       }
504       return nil;
505   }
506   return entry;
507 }
508
509 - (NSArray *)_serverCandidatesForMineqMelRoutage:(NGLdapAttribute *)attr {
510   /*
511     eg:
512       "Baluh.Hommes.Tests-Montee-En-Charge-Ogo%equipement.gouv.fr@\
513        amelie-01.ac.melanie2.i2"
514   */
515   NSMutableArray *serverCandidates;
516   unsigned i, count;
517
518   count            = [attr count];
519   serverCandidates = [NSMutableArray arrayWithCapacity:count];
520   for (i = 0; i < count; i++) {
521     NSRange  r;
522     NSString *route;
523     unsigned length;
524     unsigned start;
525     NSRange  serverNameRange;
526     NSString *serverName;
527     
528     route = [attr stringValueAtIndex:i];
529
530     /* check for melanie suffix and ignore other entries */
531     
532     r = [route rangeOfString:@".melanie2.i2" options:NSBackwardsSearch];
533     if (r.length == 0) {
534 #if 0
535       [self logWithFormat:@"found no melanie in route: '%@'", route];
536 #endif
537       continue;
538     }
539
540     /* check for @ inside the string, searching backwards (ignoring suffix) */
541     
542     // be clever: TODO: in what way is this clever?
543     length = [route length];
544     r = NSMakeRange(0, length - r.length); /* cut of suffix (.melanie2.i2) */
545     r = [route rangeOfString:@"@" options:NSBackwardsSearch range:r];
546     if (r.length == 0) {
547 #if 0
548       [self logWithFormat:@"found no @ in route: '%@'", route];
549 #endif
550       continue;
551     }
552     
553     /* check for percent sign */
554     
555     start = NSMaxRange(r); /* start behind the @ */
556     
557     /* this range covers everything after @: 'amelie-01.ac.melanie2.i2' */
558     serverNameRange = NSMakeRange(start, length - start);
559     
560     /* and this range covers everything to the @ */
561     r = NSMakeRange(0, start - 1);
562     r = [route rangeOfString:@"%" options:NSBackwardsSearch range:r];
563     if (r.length == 0) {
564 #if 0
565       [self logWithFormat:@"found no %% in route: '%@' / '%@'", 
566             route, [route substringWithRange:NSMakeRange(0, length - start)]];
567 #endif
568       continue;
569     }
570     
571     serverName = [route substringWithRange:serverNameRange];
572     [serverCandidates addObject:serverName];
573   }
574   return serverCandidates;
575 }
576
577 - (NSString *)serverFromEntry:(NGLdapEntry *)_entry {
578   NSString        *server;
579   NGLdapAttribute *attr;
580
581   server = nil;
582   
583   attr = [_entry attributeWithName:@"mineqMelRoutage"];
584   if (attr != nil) {
585     NSArray *serverCandidates;
586     
587     serverCandidates = [self _serverCandidatesForMineqMelRoutage:attr];
588     if ([serverCandidates count] > 0)
589       server = [serverCandidates objectAtIndex:0];
590     
591     if ([serverCandidates count] > 1) {
592       [self logWithFormat:
593               @"WARNING: more than one value for 'mineqMelRoutage': %@",
594               serverCandidates];
595     }
596   }
597   else {
598     [self debugWithFormat:
599             @"%s LDAP entry '%@' has no mineqMelRoutage entry?",
600             __PRETTY_FUNCTION__, [_entry dn]];
601   }
602   
603   /* last resort */
604   if (server == nil) {
605     attr = [_entry attributeWithName:@"mineqMelServeurPrincipal"];
606     if ([attr count] > 0)
607       server = [attr stringValueAtIndex:0];
608   }
609   
610   return server;
611 }
612
613 - (NSString *)primaryGetServerForAgenorUID:(NSString *)_uid {
614   /*
615    First of all : for a particular user IMAP and SMTP are served on the same
616    host.
617    
618    The name of the machine is determined by applying a regex on every values of
619    the mineqMelRoutage LDAP attribute.
620    The regex is :   .*%.*@(.*\.melanie2\.i2$)
621    It extracts the substring that follows '@', ends with 'melanie2', on
622    adresses which have a '%' before the '@'
623    
624    Example: helge.hesse%opengroupware.org@servername1.melanie2.i2
625    -> servername1.melanie2.i2
626    
627    If only one server name is found by applying the regex on every value of the
628    attribute, then this name is the IMAP/SMTP server for that user.
629    Note that this is the case when we got a unique (well formed) value for the
630    attribute.
631    If the regex finds more than one servername when applied to the differents
632    values, then the IMAP/SMTP server name is to be found in the
633    mineqMelServeurPrincipal attribute of the user.
634   */
635   NSString    *server;
636   NGLdapEntry *entry;
637   
638   if ((entry = [self _fetchEntryForAgenorUID:_uid]) == nil)
639     return nil;
640   
641   if ((server = [self serverFromEntry:entry]) != nil)
642     return server;
643   
644   [self debugWithFormat:
645           @"%s no chance of getting at server info for user '%@', "
646           @"tried everything. Sorry.",
647           __PRETTY_FUNCTION__, _uid];
648   return nil;
649 }
650
651 - (NSString *)getServerForUID:(NSString *)_uid {
652   NSString *server;
653
654   if (_uid == nil || [_uid length] == 0)
655     return nil;
656   
657   if ((server = [self _cachedServerForUID:_uid]) != nil)
658     return server;
659   
660   if (useLDAP)
661     server = [self primaryGetServerForAgenorUID:_uid];
662   else if (fallbackIMAP4Server != nil)
663     server = fallbackIMAP4Server;
664   else {
665     [self logWithFormat:@"ERROR: could not get server for uid '%@', "
666           @"neither LDAP (SOGoUserManagerUsesLDAP) nor "
667           @"a fallback (SOGoFallbackIMAP4Server) is configured.",
668           _uid];
669     server = nil;
670   }
671   
672   [self _cacheServer:server forUID:_uid];
673   return server;
674 }
675
676 /* shared mailboxes */
677
678 - (NSArray *)getSharedMailboxAccountStringsForUID:(NSString *)_uid {
679   /*
680     Sample:
681       "(&(mineqMelPartages=guizmo.g:*)(objectclass=mineqMelBoite))"
682       "guizmo.g" is the uid of the user
683     
684     Login:
685       guizmo.g.-.baluh.hommes.tests-montee-en-charge-ogo
686       (uid + ".-." + share-uid)
687     
688     Note: shared mailboxes can be on different hosts!
689   */
690   NSMutableArray   *shares = nil;
691   NGLdapConnection *conn;
692   EOQualifier      *q;
693   NSString         *sharePattern;
694   NSEnumerator     *resultEnum;
695   NGLdapEntry      *entry;
696   
697   if ([_uid length] == 0)
698     return nil;
699   
700   if (!useLDAP) {
701     [self logWithFormat:
702             @"Note: LDAP access is disabled, returning no shared accounts."];
703     return nil;
704   }
705
706   /* check cache */
707   if ((shares = [self->shareStoreCache objectForKey:_uid]) != nil)
708     return shares;
709   
710   sharePattern = [_uid stringByAppendingString:@":*"];
711   
712   q = [EOQualifier qualifierWithQualifierFormat:
713                      @"(mineqMelPartages = %@) AND (objectclass = %@)",
714                      sharePattern, shareLDAPClass];
715   
716   conn = [self ldapConnection];
717
718   resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN
719                      qualifier:q
720                      attributes:[self mailServerDiscoveryAttributes]];
721   
722   while ((entry = [resultEnum nextObject]) != nil) {
723     NSString *server, *shareLogin;
724     id shareUid;
725     
726     if ([(server = [self serverFromEntry:entry]) length] == 0) {
727       [self errorWithFormat:@"found no mail server host for share: %@",
728               [entry dn]];
729       continue;
730     }
731     
732     shareUid = [entry attributeWithName:@"uid"];
733     if ([shareUid count] < 1) {
734       [self errorWithFormat:@"found no 'uid' for share: %@", [entry dn]];
735       continue;
736     }
737     shareUid = [shareUid stringValueAtIndex:0];
738     
739     shareLogin = [_uid stringByAppendingString:shareLoginSeparator];
740     shareLogin = [shareLogin stringByAppendingString:shareUid];
741     
742     if (shares == nil)
743       shares = [NSMutableArray arrayWithCapacity:4];
744     
745     shareLogin = [shareLogin stringByAppendingString:@"@"];
746     shareLogin = [shareLogin stringByAppendingString:server];
747     [shares addObject:shareLogin];
748   }
749   
750   /* ensure that ordering is always the same */
751   [shares sortUsingSelector:@selector(compare:)];
752   
753   /* cache */
754   shares = (shares == nil) ? [NSArray array] : [[shares copy] autorelease];
755   [self->shareStoreCache addObject:shares forKey:_uid];
756   return shares;
757 }
758
759 - (NSArray *)getSharedMailboxEMailsForUID:(NSString *)_uid {
760   NSMutableArray   *shares = nil;
761   NGLdapConnection *conn;
762   EOQualifier      *q;
763   NSString         *gPattern, *cPattern;
764   NSEnumerator     *resultEnum;
765   NGLdapEntry      *entry;
766   
767   if ([_uid length] == 0)
768     return nil;
769   
770   if (!useLDAP) {
771     [self logWithFormat:
772             @"Note: LDAP access is disabled, returning no shared froms."];
773     return nil;
774   }
775   
776   /* check cache */
777   if ((shares = [self->shareEMailCache objectForKey:_uid]) != nil)
778     return shares;
779   
780   /* G and C mean "emission access" */
781   gPattern = [_uid stringByAppendingString:@":G"];
782   cPattern = [_uid stringByAppendingString:@":C"];
783   
784   q = [EOQualifier qualifierWithQualifierFormat:
785                      @"((mineqMelPartages = %@) OR (mineqMelPartages = %@)) "
786                      @"AND (objectclass = %@)",
787                    gPattern, cPattern, shareLDAPClass];
788   
789   conn = [self ldapConnection];
790   
791   resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN
792                      qualifier:q
793                      attributes:fromEMailAttrs];
794   
795   while ((entry = [resultEnum nextObject]) != nil) {
796     id emissionAttr;
797     
798     emissionAttr = [entry attributeWithName:mailEmissionAttrName];
799     if ([emissionAttr count] == 0) {
800       [self logWithFormat:@"WARNING: share has no %@ attr: %@",
801               mailEmissionAttrName, [entry dn]];
802       continue;
803     }
804     
805     if ([emissionAttr count] > 1) {
806       [self logWithFormat:
807               @"WARNING: share has more than one value in %@ attr: %@",
808               mailEmissionAttrName, [entry dn]];
809       continue;
810     }
811     
812     emissionAttr = [emissionAttr stringValueAtIndex:0];
813     if (shares == nil) shares = [NSMutableArray arrayWithCapacity:4];
814     [shares addObject:emissionAttr];
815   }
816   
817   /* ensure that ordering is always the same */
818   [shares sortUsingSelector:@selector(compare:)];
819   
820   /* cache */
821   shares = (shares == nil) ? [NSArray array] : [[shares copy] autorelease];
822   [self->shareEMailCache addObject:shares forKey:_uid];
823   return shares;
824 }
825
826 /* free busy */
827
828 - (NSURL *)getFreeBusyURLForUID:(NSString *)_uid {
829   [self logWithFormat:@"TODO(%s): implement", __PRETTY_FUNCTION__];
830   return nil;
831 }
832
833 /* defaults */
834
835 - (NSUserDefaults *)getUserDefaultsForUID:(NSString *)_uid {
836   id defaults;
837   
838   if (AgenorProfileURL == nil) {
839     [self warnWithFormat:
840             @"no profile configured, cannot retrieve defaults for user: '%@'",
841             _uid];
842     return nil;
843   }
844   
845   /* Note: do not cache, otherwise updates can be quite tricky */
846   defaults = [[[AgenorUserDefaults alloc] 
847                 initWithTableURL:AgenorProfileURL uid:_uid] autorelease];
848   return defaults;
849 }
850
851 /* internet access lock */
852
853 - (BOOL)isUserAllowedToChangeSOGoInternetAccess:(NSString *)_uid {
854   NSNumber *bv;
855   
856   bv = [self->changeInternetAccessCache objectForKey:_uid];
857   if (!bv) {
858     BOOL value;
859     
860     value = [self primaryIsUserAllowedToChangeSOGoInternetAccess:_uid];
861     bv    = [NSNumber numberWithBool:value];
862     [self->changeInternetAccessCache addObject:bv forKey:_uid];
863   }
864   return [bv boolValue];
865 }
866
867 - (BOOL)primaryIsUserAllowedToChangeSOGoInternetAccess:(NSString *)_uid {
868   static NSArray   *attrs = nil;
869   NGLdapConnection *conn;
870   EOQualifier      *q;
871   NSEnumerator     *resultEnum;
872   NGLdapEntry      *entry;
873   NGLdapAttribute  *attr;
874   NSString         *value;
875   
876   if (attrs == nil)
877     attrs = [[NSArray alloc] initWithObjects:changeInternetAccessAttrName, nil];
878   
879   q = [EOQualifier qualifierWithQualifierFormat:@"uid = %@", _uid];
880   
881   conn       = [self ldapConnection];
882   resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN
883                      qualifier:q
884                      attributes:attrs];
885   entry = [resultEnum nextObject];
886   if (entry == nil) {
887     if(debugOn) {
888       [self logWithFormat:@"%s Didn't find LDAP entry for uid '%@'!",
889         __PRETTY_FUNCTION__,
890         _uid];
891     }
892     return NO;
893   }
894   attr = [entry attributeWithName:changeInternetAccessAttrName];
895   if(attr == nil && debugOn) {
896     [self logWithFormat:@"%s LDAP entry for uid '%@' "
897                         @"has no mineqOgoAccesInternet attribute?",
898                         __PRETTY_FUNCTION__,
899                         _uid];
900     return NO; /* nothing we can do about it */
901   }
902   value = [attr stringValueAtIndex:0];
903   return [value boolValue];
904 }
905
906 /* debugging */
907
908 - (BOOL)isDebuggingEnabled {
909   return debugOn;
910 }
911
912 /* description */
913
914 - (NSString *)description {
915   NSMutableString *ms;
916   
917   ms = [NSMutableString stringWithCapacity:16];
918   [ms appendFormat:@"<0x%08X[%@]", self, NSStringFromClass([self class])];
919   [ms appendString:@">"];
920   return ms;
921 }
922
923 @end /* AgenorUserManager */