]> git.sur5r.net Git - openldap/blob - libraries/libldap/ufn.c
removed lint
[openldap] / libraries / libldap / ufn.c
1 /*
2  *  Copyright (c) 1990 Regents of the University of Michigan.
3  *  All rights reserved.
4  *
5  *  ufn.c
6  */
7
8 #ifndef lint 
9 static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n";
10 #endif
11
12 #include <stdio.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <stdlib.h>
16
17 #ifdef MACOS
18 #include "macos.h"
19 #else /* MACOS */
20 #if defined( DOS ) || defined( _WIN32 )
21 #include "msdos.h"
22 #else /* DOS */
23 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #endif /* DOS */
27 #endif /* MACOS */
28
29 #include "lber.h"
30 #include "ldap.h"
31
32 #include "ldapconfig.h"
33
34 #ifdef NEEDPROTOS
35 typedef int (*cancelptype)( void *cancelparm );
36 #else /* NEEDPROTOS */
37 typedef int (*cancelptype)();
38 #endif /* NEEDPROTOS */
39
40 #ifdef NEEDPROTOS
41 static int ldap_ufn_search_ctx( LDAP *ld, char **ufncomp, int ncomp, 
42         char *prefix, char **attrs, int attrsonly, LDAPMessage **res, 
43         cancelptype cancelproc, void *cancelparm, char *tag1, char *tag2,
44         char *tag3 );
45 static LDAPMessage *ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b );
46 static LDAPMessage *ldap_ufn_expand( LDAP *ld, cancelptype cancelproc,
47         void *cancelparm, char **dns, char *filter, int scope,
48         char **attrs, int aonly, int *err );
49 LDAPFiltDesc *ldap_ufn_setfilter( LDAP *ld, char *fname );
50 #else /* NEEDPROTOS */
51 static LDAPMessage *ldap_msg_merge();
52 static LDAPMessage *ldap_ufn_expand();
53 LDAPFiltDesc *ldap_ufn_setfilter();
54 #endif /* NEEDPROTOS */
55
56 /*
57  * ldap_ufn_search_ctx - do user friendly searching; provide cancel feature;
58  *                      specify ldapfilter.conf tags for each phase of search
59  *
60  *      ld              LDAP descriptor
61  *      ufncomp         the exploded user friendly name to look for
62  *      ncomp           number of elements in ufncomp
63  *      prefix          where to start searching
64  *      attrs           list of attribute types to return for matches
65  *      attrsonly       1 => attributes only 0 => attributes and values
66  *      res             will contain the result of the search
67  *      cancelproc      routine that returns non-zero if operation should be
68  *                      cancelled.  This can be NULL.  If it is non-NULL, the
69  *                      routine will be called periodically.
70  *      cancelparm      void * that is passed to cancelproc
71  *      tag[123]        the ldapfilter.conf tag that will be used in phases
72  *                      1, 2, and 3 of the search, respectively
73  *
74  * Example:
75  *      char            *attrs[] = { "mail", "title", 0 };
76  *      char            *ufncomp[] = { "howes", "umich", "us", 0 }
77  *      LDAPMessage     *res;
78  *      error = ldap_ufn_search_ctx( ld, ufncomp, 3, NULL, attrs, attrsonly,
79  *                      &res, acancelproc, along, "ufn first",
80  *                      "ufn intermediate", "ufn last" );
81  */
82
83 static int
84 ldap_ufn_search_ctx( LDAP *ld, char **ufncomp, int ncomp, char *prefix,
85         char **attrs, int attrsonly, LDAPMessage **res, cancelptype cancelproc,
86         void *cancelparm, char *tag1, char *tag2, char *tag3 )
87 {
88         char            *dn, *ftag = NULL;
89         char            **dns = NULL;
90         int             max, i, err, scope = 0, phase, tries;
91         LDAPFiltInfo    *fi;
92         LDAPMessage     *tmpcand;
93         LDAPMessage     *candidates;
94         LDAPMessage     *ldap_msg_merge(), *ldap_ufn_expand();
95         static char     *objattrs[] = { "objectClass", NULL };
96
97         /* 
98          * look up ufn components from most to least significant.
99          * there are 3 phases.  
100          *      phase 1 search the root for orgs or countries
101          *      phase 2 search for orgs
102          *      phase 3 search for a person
103          * in phases 1 and 2, we are building a list of candidate DNs,
104          * below which we will search for the final component of the ufn.
105          * for each component we try the filters listed in the
106          * filterconfig file, first one-level (except the last compoment),
107          * then subtree.  if any of them produce any results, we go on to
108          * the next component.
109          */
110
111         *res = NULL;
112         candidates = NULL;
113         phase = 1;
114         for ( ncomp--; ncomp != -1; ncomp-- ) {
115                 if ( *ufncomp[ncomp] == '"' ) {
116                         char    *quote;
117
118                         if ( (quote = strrchr( ufncomp[ncomp], '"' )) != NULL )
119                                 *quote = '\0';
120                         strcpy( ufncomp[ncomp], ufncomp[ncomp] + 1 );
121                 }
122                 if ( ncomp == 0 )
123                         phase = 3;
124
125                 switch ( phase ) {
126                 case 1:
127                         ftag = tag1;
128                         scope = LDAP_SCOPE_ONELEVEL;
129                         break;
130                 case 2:
131                         ftag = tag2;
132                         scope = LDAP_SCOPE_ONELEVEL;
133                         break;
134                 case 3:
135                         ftag = tag3;
136                         scope = LDAP_SCOPE_SUBTREE;
137                         break;
138                 }
139
140                 /*
141                  * construct an array of DN's to search below from the
142                  * list of candidates.
143                  */
144
145                 if ( candidates == NULL ) {
146                         if ( prefix != NULL ) {
147                                 if ( (dns = (char **) malloc( sizeof(char *)
148                                     * 2 )) == NULL ) {
149                                         return( ld->ld_errno = LDAP_NO_MEMORY );
150                                 }
151                                 dns[0] = strdup( prefix );
152                                 dns[1] = NULL;
153                         } else {
154                                 dns = NULL;
155                         }
156                 } else {
157                         i = 0, max = 0;
158                         for ( tmpcand = candidates; tmpcand != NULL &&
159                             tmpcand->lm_msgtype != LDAP_RES_SEARCH_RESULT;
160                             tmpcand = tmpcand->lm_chain )
161                         {
162                                 if ( (dn = ldap_get_dn( ld, tmpcand )) == NULL )
163                                         continue;
164
165                                 if ( dns == NULL ) {
166                                         if ( (dns = (char **) malloc(
167                                             sizeof(char *) * 8 )) == NULL ) {
168                                                 ld->ld_errno = LDAP_NO_MEMORY;
169                                                 return( LDAP_NO_MEMORY );
170                                         }
171                                         max = 8;
172                                 } else if ( i >= max ) {
173                                         if ( (dns = (char **) realloc( dns,
174                                             sizeof(char *) * 2 * max ))
175                                             == NULL )
176                                         {
177                                                 ld->ld_errno = LDAP_NO_MEMORY;
178                                                 return( LDAP_NO_MEMORY );
179                                         }
180                                         max *= 2;
181                                 }
182                                 dns[i++] = dn;
183                                 dns[i] = NULL;
184                         }
185                         ldap_msgfree( candidates );
186                         candidates = NULL;
187                 }
188                 tries = 0;
189         tryagain:
190                 tries++;
191                 for ( fi = ldap_getfirstfilter( ld->ld_filtd, ftag,
192                     ufncomp[ncomp] ); fi != NULL;
193                     fi = ldap_getnextfilter( ld->ld_filtd ) )
194                 {
195                         if ( (candidates = ldap_ufn_expand( ld, cancelproc,
196                             cancelparm, dns, fi->lfi_filter, scope,
197                             phase == 3 ? attrs : objattrs,
198                             phase == 3 ? attrsonly : 1, &err )) != NULL )
199                         {
200                                 break;
201                         }
202
203                         if ( err == -1 || err == LDAP_USER_CANCELLED ) {
204                                 if ( dns != NULL ) {
205                                         ldap_value_free( dns );
206                                         dns = NULL;
207                                 }
208                                 return( err );
209                         }
210                 }
211
212                 if ( candidates == NULL ) {
213                         if ( tries < 2 && phase != 3 ) {
214                                 scope = LDAP_SCOPE_SUBTREE;
215                                 goto tryagain;
216                         } else {
217                                 if ( dns != NULL ) {
218                                         ldap_value_free( dns );
219                                         dns = NULL;
220                                 }
221                                 return( err );
222                         }
223                 }
224
225                 /* go on to the next component */
226                 if ( phase == 1 )
227                         phase++;
228                 if ( dns != NULL ) {
229                         ldap_value_free( dns );
230                         dns = NULL;
231                 }
232         }
233         *res = candidates;
234
235         return( err );
236 }
237
238 int
239 ldap_ufn_search_ct( LDAP *ld, char *ufn, char **attrs, int attrsonly,
240         LDAPMessage **res, cancelptype cancelproc, void *cancelparm,
241         char *tag1, char *tag2, char *tag3 )
242 {
243         char    **ufncomp, **prefixcomp;
244         char    *pbuf;
245         int     ncomp, pcomp, i, err = 0;
246
247         /* initialize the getfilter stuff if it's not already */
248         if ( ld->ld_filtd == NULL && ldap_ufn_setfilter( ld, FILTERFILE )
249             == NULL ) {
250                 return( ld->ld_errno = LDAP_LOCAL_ERROR );
251         }
252
253         /* call ldap_explode_dn() to break the ufn into its components */
254         if ( (ufncomp = ldap_explode_dn( ufn, 0 )) == NULL )
255                 return( ld->ld_errno = LDAP_LOCAL_ERROR );
256         for ( ncomp = 0; ufncomp[ncomp] != NULL; ncomp++ )
257                 ;       /* NULL */
258
259         /* more than two components => try it fully qualified first */
260         if ( ncomp > 2 || ld->ld_ufnprefix == NULL ) {
261                 err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, NULL, attrs,
262                     attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 );
263
264                 if ( ldap_count_entries( ld, *res ) > 0 ) {
265                         ldap_value_free( ufncomp );
266                         return( err );
267                 } else {
268                         ldap_msgfree( *res );
269                         *res = NULL;
270                 }
271         }
272
273         if ( ld->ld_ufnprefix == NULL ) {
274                 ldap_value_free( ufncomp );
275                 return( err );
276         }
277
278         /* if that failed, or < 2 components, use the prefix */
279         if ( (prefixcomp = ldap_explode_dn( ld->ld_ufnprefix, 0 )) == NULL ) {
280                 ldap_value_free( ufncomp );
281                 return( ld->ld_errno = LDAP_LOCAL_ERROR );
282         }
283         for ( pcomp = 0; prefixcomp[pcomp] != NULL; pcomp++ )
284                 ;       /* NULL */
285         if ( (pbuf = (char *) malloc( strlen( ld->ld_ufnprefix ) + 1 ))
286             == NULL ) { 
287                 ldap_value_free( ufncomp );
288                 ldap_value_free( prefixcomp );
289                 return( ld->ld_errno = LDAP_NO_MEMORY );
290         }
291
292         for ( i = 0; i < pcomp; i++ ) {
293                 int     j;
294
295                 *pbuf = '\0';
296                 for ( j = i; j < pcomp; j++ ) {
297                         strcat( pbuf, prefixcomp[j] );
298                         if ( j + 1 < pcomp )
299                                 strcat( pbuf, "," );
300                 }
301                 err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, pbuf, attrs,
302                     attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 );
303
304                 if ( ldap_count_entries( ld, *res ) > 0 ) {
305                         break;
306                 } else {
307                         ldap_msgfree( *res );
308                         *res = NULL;
309                 }
310         }
311
312         ldap_value_free( ufncomp );
313         ldap_value_free( prefixcomp );
314         free( pbuf );
315
316         return( err );
317 }
318
319 /*
320  * same as ldap_ufn_search_ct, except without the ability to specify
321  * ldapfilter.conf tags.
322  */
323 int
324 ldap_ufn_search_c( LDAP *ld, char *ufn, char **attrs, int attrsonly,
325         LDAPMessage **res, cancelptype cancelproc, void *cancelparm )
326 {
327         return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res, cancelproc,
328             cancelparm, "ufn first", "ufn intermediate", "ufn last" ) );
329 }
330
331 /*
332  * same as ldap_ufn_search_c without the cancel function
333  */
334 int
335 ldap_ufn_search_s( LDAP *ld, char *ufn, char **attrs, int attrsonly,
336         LDAPMessage **res )
337 {
338         struct timeval  tv;
339
340         tv.tv_sec = ld->ld_timelimit;
341
342         return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res,
343                 ld->ld_timelimit ? ldap_ufn_timeout : NULL,
344                 ld->ld_timelimit ? (void *) &tv : NULL,
345                 "ufn first", "ufn intermediate", "ufn last" ) );
346 }
347
348
349 /*
350  * ldap_msg_merge - merge two ldap search result chains.  the more
351  * serious of the two error result codes is kept.
352  */
353
354 static LDAPMessage *
355 ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b )
356 {
357         LDAPMessage     *end, *aprev, *aend, *bprev, *bend;
358
359         if ( a == NULL )
360                 return( b );
361
362         if ( b == NULL )
363                 return( a );
364
365         /* find the ends of the a and b chains */
366         aprev = NULL;
367         for ( aend = a; aend->lm_chain != NULL; aend = aend->lm_chain )
368                 aprev = aend;
369         bprev = NULL;
370         for ( bend = b; bend->lm_chain != NULL; bend = bend->lm_chain )
371                 bprev = bend;
372
373         /* keep result a */
374         if ( ldap_result2error( ld, aend, 0 ) != LDAP_SUCCESS ) {
375                 /* remove result b */
376                 ldap_msgfree( bend );
377                 if ( bprev != NULL )
378                         bprev->lm_chain = NULL;
379                 else
380                         b = NULL;
381                 end = aend;
382                 if ( aprev != NULL )
383                         aprev->lm_chain = NULL;
384                 else
385                         a = NULL;
386         /* keep result b */
387         } else {
388                 /* remove result a */
389                 ldap_msgfree( aend );
390                 if ( aprev != NULL )
391                         aprev->lm_chain = NULL;
392                 else
393                         a = NULL;
394                 end = bend;
395                 if ( bprev != NULL )
396                         bprev->lm_chain = NULL;
397                 else
398                         b = NULL;
399         }
400
401         if ( (a == NULL && b == NULL) || (a == NULL && bprev == NULL) ||
402             (b == NULL && aprev == NULL) )
403                 return( end );
404
405         if ( a == NULL ) {
406                 bprev->lm_chain = end;
407                 return( b );
408         } else if ( b == NULL ) {
409                 aprev->lm_chain = end;
410                 return( a );
411         } else {
412                 bprev->lm_chain = end;
413                 aprev->lm_chain = b;
414                 return( a );
415         }
416 }
417
418 static LDAPMessage *
419 ldap_ufn_expand( LDAP *ld, cancelptype cancelproc, void *cancelparm,
420         char **dns, char *filter, int scope, char **attrs, int aonly,
421         int *err )
422 {
423         LDAPMessage     *tmpcand, *tmpres;
424         char            *dn;
425         int             i, msgid;
426         struct timeval  tv;
427
428         /* search for this component below the current candidates */
429         tmpcand = NULL;
430         i = 0;
431         do {
432                 if ( dns != NULL )
433                         dn = dns[i];
434                 else
435                         dn = "";
436
437                 if (( msgid = ldap_search( ld, dn, scope, filter, attrs,
438                     aonly )) == -1 ) {
439                         ldap_msgfree( tmpcand );
440                         *err = ld->ld_errno;
441                         return( NULL );
442                 }
443
444                 tv.tv_sec = 0;
445                 tv.tv_usec = 100000;    /* 1/10 of a second */
446
447                 do {
448                         *err = ldap_result( ld, msgid, 1, &tv, &tmpres );
449                         if ( *err == 0 && cancelproc != NULL &&
450                             (*cancelproc)( cancelparm ) != 0 ) {
451                                 ldap_abandon( ld, msgid );
452                                 *err = LDAP_USER_CANCELLED;
453                                 ld->ld_errno = LDAP_USER_CANCELLED;
454                         }
455                 } while ( *err == 0 );
456
457                 if ( *err == LDAP_USER_CANCELLED || *err < 0 ||
458                     ( *err = ldap_result2error( ld, tmpres, 0 )) == -1 ) {
459                         ldap_msgfree( tmpcand );
460                         return( NULL );
461                 }
462                 
463                 tmpcand = ldap_msg_merge( ld, tmpcand, tmpres );
464
465                 i++;
466         } while ( dns != NULL && dns[i] != NULL );
467
468         if ( ldap_count_entries( ld, tmpcand ) > 0 ) {
469                 return( tmpcand );
470         } else {
471                 ldap_msgfree( tmpcand );
472                 return( NULL );
473         }
474 }
475
476 /*
477  * ldap_ufn_setfilter - set the filter config file used in ufn searching
478  */
479
480 LDAPFiltDesc *
481 ldap_ufn_setfilter( LDAP *ld, char *fname )
482 {
483         if ( ld->ld_filtd != NULL )
484                 ldap_getfilter_free( ld->ld_filtd );
485
486         return( ld->ld_filtd = ldap_init_getfilter( fname ) );
487 }
488
489 void
490 ldap_ufn_setprefix( LDAP *ld, char *prefix )
491 {
492         if ( ld->ld_ufnprefix != NULL )
493                 free( ld->ld_ufnprefix );
494
495         ld->ld_ufnprefix = strdup( prefix );
496 }
497
498 int
499 ldap_ufn_timeout( void *tvparam )
500 {
501         struct timeval  *tv;
502
503         tv = (struct timeval *)tvparam;
504
505         if ( tv->tv_sec != 0 ) {
506                 tv->tv_usec = tv->tv_sec * 1000000;     /* sec => micro sec */
507                 tv->tv_sec = 0;
508         }
509         tv->tv_usec -= 100000;  /* 1/10 of a second */
510
511         return( tv->tv_usec <= 0 ? 1 : 0 );
512 }