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