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