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