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