Back to index

lightning-sunbird  0.9+nobinonly
ufn.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla Communicator client code, released
00015  * March 31, 1998.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 /*
00038  *  Copyright (c) 1990 Regents of the University of Michigan.
00039  *  All rights reserved.
00040  */
00041 /*
00042  *  ufn.c
00043  */
00044 
00045 #if 0
00046 #ifndef lint 
00047 static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n";
00048 #endif
00049 #endif
00050 
00051 #include "ldap-int.h"
00052 
00053 typedef int (LDAP_CALL *cancelptype)( void *cancelparm );
00054 
00055 static int ldap_ufn_search_ctx( LDAP *ld, char **ufncomp, int ncomp, 
00056        char *prefix, char **attrs, int attrsonly,
00057        LDAPMessage **res, LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm,
00058        char *tag1, char *tag2, char *tag3 );
00059 static LDAPMessage *ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b );
00060 static LDAPMessage *ldap_ufn_expand( LDAP *ld, 
00061        LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm, char **dns, 
00062        char *filter, int scope, char **attrs, int aonly, int *err );
00063 
00064 /*
00065  * ldap_ufn_search_ctx - do user friendly searching; provide cancel feature;
00066  *                   specify ldapfilter.conf tags for each phase of search
00067  *
00068  *     ld            LDAP descriptor
00069  *     ufncomp              the exploded user friendly name to look for
00070  *     ncomp         number of elements in ufncomp
00071  *     prefix        where to start searching
00072  *     attrs         list of attribute types to return for matches
00073  *     attrsonly     1 => attributes only 0 => attributes and values
00074  *     res           will contain the result of the search
00075  *     cancelproc    routine that returns non-zero if operation should be
00076  *                   cancelled.  This can be NULL.  If it is non-NULL, the
00077  *                   routine will be called periodically.
00078  *     cancelparm    void * that is passed to cancelproc
00079  *     tag[123]      the ldapfilter.conf tag that will be used in phases
00080  *                   1, 2, and 3 of the search, respectively
00081  *
00082  * Example:
00083  *     char          *attrs[] = { "mail", "title", 0 };
00084  *     char          *ufncomp[] = { "howes", "umich", "us", 0 }
00085  *     LDAPMessage   *res;
00086  *     error = ldap_ufn_search_ctx( ld, ufncomp, 3, NULL, attrs, attrsonly,
00087  *                   &res, acancelproc, along, "ufn first",
00088  *                   "ufn intermediate", "ufn last" );
00089  */
00090 
00091 static int
00092 ldap_ufn_search_ctx(
00093     LDAP      *ld, 
00094     char      **ufncomp, 
00095     int       ncomp, 
00096     char      *prefix,
00097     char      **attrs,
00098     int       attrsonly, 
00099     LDAPMessage **res, 
00100     LDAP_CANCELPROC_CALLBACK *cancelproc,
00101     void      *cancelparm, 
00102     char      *tag1, 
00103     char      *tag2, 
00104     char      *tag3
00105 )
00106 {
00107        char          *dn, *ftag = NULL;
00108        char          **dns = NULL;
00109        int           max, i, err, scope = 0, phase, tries;
00110        LDAPFiltInfo  *fi;
00111        LDAPMessage   *tmpcand;
00112        LDAPMessage   *candidates;
00113        static char   *objattrs[] = { "objectClass", NULL };
00114 
00115        /* 
00116         * look up ufn components from most to least significant.
00117         * there are 3 phases.  
00118         *     phase 1       search the root for orgs or countries
00119         *     phase 2       search for orgs
00120         *     phase 3       search for a person
00121         * in phases 1 and 2, we are building a list of candidate DNs,
00122         * below which we will search for the final component of the ufn.
00123         * for each component we try the filters listed in the
00124         * filterconfig file, first one-level (except the last compoment),
00125         * then subtree.  if any of them produce any results, we go on to
00126         * the next component.
00127         */
00128 
00129        *res = NULL;
00130        candidates = NULL;
00131        phase = 1;
00132        for ( ncomp--; ncomp != -1; ncomp-- ) {
00133               if ( *ufncomp[ncomp] == '"' ) {
00134                      char   *quote;
00135 
00136                      if ( (quote = strrchr( ufncomp[ncomp], '"' )) != NULL )
00137                             *quote = '\0';
00138                      strcpy( ufncomp[ncomp], ufncomp[ncomp] + 1 );
00139               }
00140               if ( ncomp == 0 )
00141                      phase = 3;
00142 
00143               switch ( phase ) {
00144               case 1:
00145                      ftag = tag1;
00146                      scope = LDAP_SCOPE_ONELEVEL;
00147                      break;
00148               case 2:
00149                      ftag = tag2;
00150                      scope = LDAP_SCOPE_ONELEVEL;
00151                      break;
00152               case 3:
00153                      ftag = tag3;
00154                      scope = LDAP_SCOPE_SUBTREE;
00155                      break;
00156               }
00157 
00158               /*
00159                * construct an array of DN's to search below from the
00160                * list of candidates.
00161                */
00162 
00163               if ( candidates == NULL ) {
00164                      if ( prefix != NULL ) {
00165                             if ( (dns = (char **)NSLDAPI_MALLOC(
00166                                 sizeof(char *) * 2 )) == NULL ) {
00167                                    err = LDAP_NO_MEMORY;
00168                                    LDAP_SET_LDERRNO( ld, err, NULL, NULL );
00169                                    return( err );
00170                             }
00171                             dns[0] = nsldapi_strdup( prefix );
00172                             dns[1] = NULL;
00173                      } else {
00174                             dns = NULL;
00175                      }
00176               } else {
00177                      i = 0, max = 0;
00178                      for ( tmpcand = candidates; tmpcand != NULL &&
00179                          tmpcand->lm_msgtype != LDAP_RES_SEARCH_RESULT;
00180                          tmpcand = tmpcand->lm_chain )
00181                      {
00182                             if ( (dn = ldap_get_dn( ld, tmpcand )) == NULL )
00183                                    continue;
00184 
00185                             if ( dns == NULL ) {
00186                                    if ( (dns = (char **)NSLDAPI_MALLOC(
00187                                        sizeof(char *) * 8 )) == NULL ) {
00188                                           err = LDAP_NO_MEMORY;
00189                                           LDAP_SET_LDERRNO( ld, err,
00190                                               NULL, NULL );
00191                                           return( err );
00192                                    }
00193                                    max = 8;
00194                             } else if ( i >= max ) {
00195                                    if ( (dns = (char **)NSLDAPI_REALLOC(
00196                                        dns, sizeof(char *) * 2 * max ))
00197                                        == NULL ) {
00198                                           err = LDAP_NO_MEMORY;
00199                                           LDAP_SET_LDERRNO( ld, err,
00200                                               NULL, NULL );
00201                                           return( err );
00202                                    }
00203                                    max *= 2;
00204                             }
00205                             dns[i++] = dn;
00206                             dns[i] = NULL;
00207                      }
00208                      ldap_msgfree( candidates );
00209                      candidates = NULL;
00210               }
00211               tries = 0;
00212        tryagain:
00213               tries++;
00214               for ( fi = ldap_getfirstfilter( ld->ld_filtd, ftag,
00215                   ufncomp[ncomp] ); fi != NULL;
00216                   fi = ldap_getnextfilter( ld->ld_filtd ) )
00217               {
00218                      if ( (candidates = ldap_ufn_expand( ld, cancelproc,
00219                          cancelparm, dns, fi->lfi_filter, scope,
00220                          phase == 3 ? attrs : objattrs,
00221                          phase == 3 ? attrsonly : 1, &err )) != NULL )
00222                      {
00223                             break;
00224                      }
00225 
00226                      if ( err == -1 || err == LDAP_USER_CANCELLED ) {
00227                             if ( dns != NULL ) {
00228                                    ldap_value_free( dns );
00229                                    dns = NULL;
00230                             }
00231                             return( err );
00232                      }
00233               }
00234 
00235               if ( candidates == NULL ) {
00236                      if ( tries < 2 && phase != 3 ) {
00237                             scope = LDAP_SCOPE_SUBTREE;
00238                             goto tryagain;
00239                      } else {
00240                             if ( dns != NULL ) {
00241                                    ldap_value_free( dns );
00242                                    dns = NULL;
00243                             }
00244                             return( err );
00245                      }
00246               }
00247 
00248               /* go on to the next component */
00249               if ( phase == 1 )
00250                      phase++;
00251               if ( dns != NULL ) {
00252                      ldap_value_free( dns );
00253                      dns = NULL;
00254               }
00255        }
00256        *res = candidates;
00257 
00258        return( err );
00259 }
00260 
00261 int
00262 LDAP_CALL
00263 ldap_ufn_search_ct( LDAP *ld, char *ufn, char **attrs, int attrsonly,
00264        LDAPMessage **res, LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm,
00265        char *tag1, char *tag2, char *tag3 )
00266 {
00267        char   **ufncomp, **prefixcomp;
00268        char   *pbuf;
00269        int    ncomp, pcomp, i, err = 0;
00270 
00271        /* getfilter stuff must be inited before we are called */
00272        if ( ld->ld_filtd == NULL ) {
00273               err = LDAP_PARAM_ERROR;
00274               LDAP_SET_LDERRNO( ld, err, NULL, NULL );
00275               return( err );
00276        }
00277 
00278        /* call ldap_explode_dn() to break the ufn into its components */
00279        if ( (ufncomp = ldap_explode_dn( ufn, 0 )) == NULL ) {
00280               err = LDAP_LOCAL_ERROR;
00281               LDAP_SET_LDERRNO( ld, err, NULL, NULL );
00282               return( err );
00283        }
00284        for ( ncomp = 0; ufncomp[ncomp] != NULL; ncomp++ )
00285               ;      /* NULL */
00286 
00287        /* more than two components => try it fully qualified first */
00288        if ( ncomp > 2 || ld->ld_ufnprefix == NULL ) {
00289               err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, NULL, attrs,
00290                   attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 );
00291 
00292               if ( ldap_count_entries( ld, *res ) > 0 ) {
00293                      ldap_value_free( ufncomp );
00294                      return( err );
00295               } else {
00296                      ldap_msgfree( *res );
00297                      *res = NULL;
00298               }
00299        }
00300 
00301        if ( ld->ld_ufnprefix == NULL ) {
00302               ldap_value_free( ufncomp );
00303               return( err );
00304        }
00305 
00306        /* if that failed, or < 2 components, use the prefix */
00307        if ( (prefixcomp = ldap_explode_dn( ld->ld_ufnprefix, 0 )) == NULL ) {
00308               ldap_value_free( ufncomp );
00309               err = LDAP_LOCAL_ERROR;
00310               LDAP_SET_LDERRNO( ld, err, NULL, NULL );
00311               return( err );
00312        }
00313        for ( pcomp = 0; prefixcomp[pcomp] != NULL; pcomp++ )
00314               ;      /* NULL */
00315        if ( (pbuf = (char *)NSLDAPI_MALLOC( strlen( ld->ld_ufnprefix ) + 1 ))
00316            == NULL ) {      
00317               ldap_value_free( ufncomp );
00318               ldap_value_free( prefixcomp );
00319               err = LDAP_NO_MEMORY;
00320               LDAP_SET_LDERRNO( ld, err, NULL, NULL );
00321               return( err );
00322        }
00323 
00324        for ( i = 0; i < pcomp; i++ ) {
00325               int    j;
00326 
00327               *pbuf = '\0';
00328               for ( j = i; j < pcomp; j++ ) {
00329                      strcat( pbuf, prefixcomp[j] );
00330                      if ( j + 1 < pcomp )
00331                             strcat( pbuf, "," );
00332               }
00333               err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, pbuf, attrs,
00334                   attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 );
00335 
00336               if ( ldap_count_entries( ld, *res ) > 0 ) {
00337                      break;
00338               } else {
00339                      ldap_msgfree( *res );
00340                      *res = NULL;
00341               }
00342        }
00343 
00344        ldap_value_free( ufncomp );
00345        ldap_value_free( prefixcomp );
00346        NSLDAPI_FREE( pbuf );
00347 
00348        return( err );
00349 }
00350 
00351 /*
00352  * same as ldap_ufn_search_ct, except without the ability to specify
00353  * ldapfilter.conf tags.
00354  */
00355 int
00356 LDAP_CALL
00357 ldap_ufn_search_c( LDAP *ld, char *ufn, char **attrs, int attrsonly,
00358        LDAPMessage **res, LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm )
00359 {
00360        return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res, cancelproc,
00361            cancelparm, "ufn first", "ufn intermediate", "ufn last" ) );
00362 }
00363 
00364 /*
00365  * same as ldap_ufn_search_c without the cancel function
00366  */
00367 int
00368 LDAP_CALL
00369 ldap_ufn_search_s( LDAP *ld, char *ufn, char **attrs, int attrsonly,
00370        LDAPMessage **res )
00371 {
00372        struct timeval       tv;
00373 
00374        tv.tv_sec = ld->ld_timelimit;
00375 
00376        return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res,
00377               ld->ld_timelimit ? ldap_ufn_timeout : NULL,
00378               ld->ld_timelimit ? (void *) &tv : NULL,
00379               "ufn first", "ufn intermediate", "ufn last" ) );
00380 }
00381 
00382 
00383 /*
00384  * ldap_msg_merge - merge two ldap search result chains.  the more
00385  * serious of the two error result codes is kept.
00386  */
00387 
00388 static LDAPMessage *
00389 ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b )
00390 {
00391        LDAPMessage   *end, *aprev, *aend, *bprev, *bend;
00392 
00393        if ( a == NULL )
00394               return( b );
00395 
00396        if ( b == NULL )
00397               return( a );
00398 
00399        /* find the ends of the a and b chains */
00400        aprev = NULL;
00401        for ( aend = a; aend->lm_chain != NULL; aend = aend->lm_chain )
00402               aprev = aend;
00403        bprev = NULL;
00404        for ( bend = b; bend->lm_chain != NULL; bend = bend->lm_chain )
00405               bprev = bend;
00406 
00407        /* keep result a */
00408        if ( ldap_result2error( ld, aend, 0 ) != LDAP_SUCCESS ) {
00409               /* remove result b */
00410               ldap_msgfree( bend );
00411               if ( bprev != NULL )
00412                      bprev->lm_chain = NULL;
00413               else
00414                      b = NULL;
00415               end = aend;
00416               if ( aprev != NULL )
00417                      aprev->lm_chain = NULL;
00418               else
00419                      a = NULL;
00420        /* keep result b */
00421        } else {
00422               /* remove result a */
00423               ldap_msgfree( aend );
00424               if ( aprev != NULL )
00425                      aprev->lm_chain = NULL;
00426               else
00427                      a = NULL;
00428               end = bend;
00429               if ( bprev != NULL )
00430                      bprev->lm_chain = NULL;
00431               else
00432                      b = NULL;
00433        }
00434 
00435        if ( (a == NULL && b == NULL) || (a == NULL && bprev == NULL) ||
00436            (b == NULL && aprev == NULL) )
00437               return( end );
00438 
00439        if ( a == NULL ) {
00440               bprev->lm_chain = end;
00441               return( b );
00442        } else if ( b == NULL ) {
00443               aprev->lm_chain = end;
00444               return( a );
00445        } else {
00446               bprev->lm_chain = end;
00447               aprev->lm_chain = b;
00448               return( a );
00449        }
00450 }
00451 
00452 static LDAPMessage *
00453 ldap_ufn_expand( LDAP *ld, LDAP_CANCELPROC_CALLBACK *cancelproc, 
00454        void *cancelparm, char **dns, char *filter, int scope, 
00455        char **attrs, int aonly, int *err )
00456 {
00457        LDAPMessage   *tmpcand, *tmpres;
00458        char          *dn;
00459        int           i, msgid;
00460        struct timeval       tv;
00461 
00462        /* search for this component below the current candidates */
00463        tmpcand = NULL;
00464        i = 0;
00465        do {
00466               if ( dns != NULL )
00467                      dn = dns[i];
00468               else
00469                      dn = "";
00470 
00471               if (( msgid = ldap_search( ld, dn, scope, filter, attrs,
00472                   aonly )) == -1 ) {
00473                      ldap_msgfree( tmpcand );
00474                      *err = LDAP_GET_LDERRNO( ld, NULL, NULL );
00475                      return( NULL );
00476               }
00477 
00478               tv.tv_sec = 0;
00479               tv.tv_usec = 100000; /* 1/10 of a second */
00480 
00481               do {
00482                      *err = ldap_result( ld, msgid, 1, &tv, &tmpres );
00483                      if ( *err == 0 && cancelproc != NULL &&
00484                          (*cancelproc)( cancelparm ) != 0 ) {
00485                             ldap_abandon( ld, msgid );
00486                             *err = LDAP_USER_CANCELLED;
00487                             LDAP_SET_LDERRNO( ld, *err, NULL, NULL );
00488                      }
00489               } while ( *err == 0 );
00490 
00491               if ( *err == LDAP_USER_CANCELLED || *err < 0 ||
00492                   ( *err = ldap_result2error( ld, tmpres, 0 )) == -1 ) {
00493                      ldap_msgfree( tmpcand );
00494                      return( NULL );
00495               }
00496               
00497               tmpcand = ldap_msg_merge( ld, tmpcand, tmpres );
00498 
00499               i++;
00500        } while ( dns != NULL && dns[i] != NULL );
00501 
00502        if ( ldap_count_entries( ld, tmpcand ) > 0 ) {
00503               return( tmpcand );
00504        } else {
00505               ldap_msgfree( tmpcand );
00506               return( NULL );
00507        }
00508 }
00509 
00510 /*
00511  * ldap_ufn_setfilter - set the filter config file used in ufn searching
00512  */
00513 
00514 LDAPFiltDesc *
00515 LDAP_CALL
00516 ldap_ufn_setfilter( LDAP *ld, char *fname )
00517 {
00518        if ( ld->ld_filtd != NULL )
00519               ldap_getfilter_free( ld->ld_filtd );
00520 
00521        return( ld->ld_filtd = ldap_init_getfilter( fname ) );
00522 }
00523 
00524 void
00525 LDAP_CALL
00526 ldap_ufn_setprefix( LDAP *ld, char *prefix )
00527 {
00528        if ( ld->ld_ufnprefix != NULL )
00529               NSLDAPI_FREE( ld->ld_ufnprefix );
00530 
00531        ld->ld_ufnprefix = nsldapi_strdup( prefix );
00532 }
00533 
00534 int
00535 LDAP_C
00536 ldap_ufn_timeout( void *tvparam )
00537 {
00538        struct timeval       *tv;
00539 
00540        tv = (struct timeval *)tvparam;
00541 
00542        if ( tv->tv_sec != 0 ) {
00543               tv->tv_usec = tv->tv_sec * 1000000;       /* sec => micro sec */
00544               tv->tv_sec = 0;
00545        }
00546        tv->tv_usec -= 100000;      /* 1/10 of a second */
00547 
00548        return( tv->tv_usec <= 0 ? 1 : 0 );
00549 }