Back to index

openldap  2.4.31
valsort.c
Go to the documentation of this file.
00001 /* valsort.c - sort attribute values */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2005-2012 The OpenLDAP Foundation.
00006  * Portions copyright 2005 Symas Corporation.
00007  * All rights reserved.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted only as authorized by the OpenLDAP
00011  * Public License.
00012  *
00013  * A copy of this license is available in the file LICENSE in the
00014  * top-level directory of the distribution or, alternatively, at
00015  * <http://www.OpenLDAP.org/license.html>.
00016  */
00017 /* ACKNOWLEDGEMENTS:
00018  * This work was initially developed by Howard Chu for inclusion in
00019  * OpenLDAP Software. This work was sponsored by Stanford University.
00020  */
00021 
00022 /*
00023  * This overlay sorts the values of multi-valued attributes when returning
00024  * them in a search response.
00025  */
00026 #include "portable.h"
00027 
00028 #ifdef SLAPD_OVER_VALSORT
00029 
00030 #include <stdio.h>
00031 
00032 #include <ac/string.h>
00033 #include <ac/ctype.h>
00034 
00035 #include "slap.h"
00036 #include "config.h"
00037 #include "lutil.h"
00038 
00039 #define       VALSORT_ASCEND       0
00040 #define       VALSORT_DESCEND      1
00041 
00042 #define       VALSORT_ALPHA 2
00043 #define       VALSORT_NUMERIC      4
00044 
00045 #define       VALSORT_WEIGHTED     8
00046 
00047 typedef struct valsort_info {
00048        struct valsort_info *vi_next;
00049        struct berval vi_dn;
00050        AttributeDescription *vi_ad;
00051        slap_mask_t vi_sort;
00052 } valsort_info;
00053 
00054 static int valsort_cid;
00055 
00056 static ConfigDriver valsort_cf_func;
00057 
00058 static ConfigTable valsort_cfats[] = {
00059        { "valsort-attr", "attribute> <dn> <sort-type", 4, 5, 0, ARG_MAGIC,
00060               valsort_cf_func, "( OLcfgOvAt:5.1 NAME 'olcValSortAttr' "
00061                      "DESC 'Sorting rule for attribute under given DN' "
00062                      "EQUALITY caseIgnoreMatch "
00063                      "SYNTAX OMsDirectoryString )", NULL, NULL },
00064        { NULL }
00065 };
00066 
00067 static ConfigOCs valsort_cfocs[] = {
00068        { "( OLcfgOvOc:5.1 "
00069               "NAME 'olcValSortConfig' "
00070               "DESC 'Value Sorting configuration' "
00071               "SUP olcOverlayConfig "
00072               "MUST olcValSortAttr )",
00073                      Cft_Overlay, valsort_cfats },
00074        { NULL }
00075 };
00076 
00077 static slap_verbmasks sorts[] = {
00078        { BER_BVC("alpha-ascend"), VALSORT_ASCEND|VALSORT_ALPHA },
00079        { BER_BVC("alpha-descend"), VALSORT_DESCEND|VALSORT_ALPHA },
00080        { BER_BVC("numeric-ascend"), VALSORT_ASCEND|VALSORT_NUMERIC },
00081        { BER_BVC("numeric-descend"), VALSORT_DESCEND|VALSORT_NUMERIC },
00082        { BER_BVC("weighted"), VALSORT_WEIGHTED },
00083        { BER_BVNULL, 0 }
00084 };
00085 
00086 static Syntax *syn_numericString;
00087 
00088 static int
00089 valsort_cf_func(ConfigArgs *c) {
00090        slap_overinst *on = (slap_overinst *)c->bi;
00091        valsort_info vitmp, *vi;
00092        const char *text = NULL;
00093        int i, is_numeric;
00094        struct berval bv = BER_BVNULL;
00095 
00096        if ( c->op == SLAP_CONFIG_EMIT ) {
00097               for ( vi = on->on_bi.bi_private; vi; vi = vi->vi_next ) {
00098                      struct berval bv2 = BER_BVNULL, bvret;
00099                      char *ptr;
00100                      int len;
00101                      
00102                      len = vi->vi_ad->ad_cname.bv_len + 1 + vi->vi_dn.bv_len + 2;
00103                      i = vi->vi_sort;
00104                      if ( i & VALSORT_WEIGHTED ) {
00105                             enum_to_verb( sorts, VALSORT_WEIGHTED, &bv2 );
00106                             len += bv2.bv_len + 1;
00107                             i ^= VALSORT_WEIGHTED;
00108                      }
00109                      if ( i ) {
00110                             enum_to_verb( sorts, i, &bv );
00111                             len += bv.bv_len + 1;
00112                      }
00113                      bvret.bv_val = ch_malloc( len+1 );
00114                      bvret.bv_len = len;
00115 
00116                      ptr = lutil_strcopy( bvret.bv_val, vi->vi_ad->ad_cname.bv_val );
00117                      *ptr++ = ' ';
00118                      *ptr++ = '"';
00119                      ptr = lutil_strcopy( ptr, vi->vi_dn.bv_val );
00120                      *ptr++ = '"';
00121                      if ( vi->vi_sort & VALSORT_WEIGHTED ) {
00122                             *ptr++ = ' ';
00123                             ptr = lutil_strcopy( ptr, bv2.bv_val );
00124                      }
00125                      if ( i ) {
00126                             *ptr++ = ' ';
00127                             strcpy( ptr, bv.bv_val );
00128                      }
00129                      ber_bvarray_add( &c->rvalue_vals, &bvret );
00130               }
00131               i = ( c->rvalue_vals != NULL ) ? 0 : 1;
00132               return i;
00133        } else if ( c->op == LDAP_MOD_DELETE ) {
00134               if ( c->valx < 0 ) {
00135                      for ( vi = on->on_bi.bi_private; vi; vi = on->on_bi.bi_private ) {
00136                             on->on_bi.bi_private = vi->vi_next;
00137                             ch_free( vi->vi_dn.bv_val );
00138                             ch_free( vi );
00139                      }
00140               } else {
00141                      valsort_info **prev;
00142 
00143                      for (i=0, prev = (valsort_info **)&on->on_bi.bi_private,
00144                             vi = *prev; vi && i<c->valx;
00145                             prev = &vi->vi_next, vi = vi->vi_next, i++ );
00146                      (*prev)->vi_next = vi->vi_next;
00147                      ch_free( vi->vi_dn.bv_val );
00148                      ch_free( vi );
00149               }
00150               return 0;
00151        }
00152        vitmp.vi_ad = NULL;
00153        i = slap_str2ad( c->argv[1], &vitmp.vi_ad, &text );
00154        if ( i ) {
00155               snprintf( c->cr_msg, sizeof( c->cr_msg), "<%s> %s", c->argv[0], text );
00156               Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
00157                      c->log, c->cr_msg, c->argv[1] );
00158               return(1);
00159        }
00160        if ( is_at_single_value( vitmp.vi_ad->ad_type )) {
00161               snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> %s is single-valued, ignoring", c->argv[0],
00162                      vitmp.vi_ad->ad_cname.bv_val );
00163               Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
00164                      c->log, c->cr_msg, c->argv[1] );
00165               return(0);
00166        }
00167        is_numeric = ( vitmp.vi_ad->ad_type->sat_syntax == syn_numericString ||
00168               vitmp.vi_ad->ad_type->sat_syntax == slap_schema.si_syn_integer ) ? 1
00169               : 0;
00170        ber_str2bv( c->argv[2], 0, 0, &bv );
00171        i = dnNormalize( 0, NULL, NULL, &bv, &vitmp.vi_dn, NULL );
00172        if ( i ) {
00173               snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unable to normalize DN", c->argv[0] );
00174               Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
00175                      c->log, c->cr_msg, c->argv[2] );
00176               return(1);
00177        }
00178        i = verb_to_mask( c->argv[3], sorts );
00179        if ( BER_BVISNULL( &sorts[i].word )) {
00180               snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unrecognized sort type", c->argv[0] );
00181               Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
00182                      c->log, c->cr_msg, c->argv[3] );
00183               return(1);
00184        }
00185        vitmp.vi_sort = sorts[i].mask;
00186        if ( sorts[i].mask == VALSORT_WEIGHTED && c->argc == 5 ) {
00187               i = verb_to_mask( c->argv[4], sorts );
00188               if ( BER_BVISNULL( &sorts[i].word )) {
00189                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unrecognized sort type", c->argv[0] );
00190                      Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
00191                             c->log, c->cr_msg, c->argv[4] );
00192                      return(1);
00193               }
00194               vitmp.vi_sort |= sorts[i].mask;
00195        }
00196        if (( vitmp.vi_sort & VALSORT_NUMERIC ) && !is_numeric ) {
00197               snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> numeric sort specified for non-numeric syntax",
00198                      c->argv[0] );
00199               Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
00200                      c->log, c->cr_msg, c->argv[1] );
00201               return(1);
00202        }
00203        vi = ch_malloc( sizeof(valsort_info) );
00204        *vi = vitmp;
00205        vi->vi_next = on->on_bi.bi_private;
00206        on->on_bi.bi_private = vi;
00207        return 0;
00208 }
00209 
00210 /* Use Insertion Sort algorithm on selected values */
00211 static void
00212 do_sort( Operation *op, Attribute *a, int beg, int num, slap_mask_t sort )
00213 {
00214        int i, j, gotnvals;
00215        struct berval tmp, ntmp, *vals = NULL, *nvals;
00216 
00217        gotnvals = (a->a_vals != a->a_nvals );
00218 
00219        nvals = a->a_nvals + beg;
00220        if ( gotnvals )
00221               vals = a->a_vals + beg;
00222 
00223        if ( sort & VALSORT_NUMERIC ) {
00224               long *numbers = op->o_tmpalloc( num * sizeof(long), op->o_tmpmemctx ),
00225                      idx;
00226               for (i=0; i<num; i++)
00227                      numbers[i] = strtol( nvals[i].bv_val, NULL, 0 );
00228 
00229               for (i=1; i<num; i++) {
00230                      idx = numbers[i];
00231                      ntmp = nvals[i];
00232                      if ( gotnvals ) tmp = vals[i];
00233                      j = i;
00234                      while ( j>0 ) {
00235                             int cmp = (sort & VALSORT_DESCEND) ? numbers[j-1] < idx :
00236                                    numbers[j-1] > idx;
00237                             if ( !cmp ) break;
00238                             numbers[j] = numbers[j-1];
00239                             nvals[j] = nvals[j-1];
00240                             if ( gotnvals ) vals[j] = vals[j-1];
00241                             j--;
00242                      }
00243                      numbers[j] = idx;
00244                      nvals[j] = ntmp;
00245                      if ( gotnvals ) vals[j] = tmp;
00246               }
00247               op->o_tmpfree( numbers, op->o_tmpmemctx );
00248        } else {
00249               for (i=1; i<num; i++) {
00250                      ntmp = nvals[i];
00251                      if ( gotnvals ) tmp = vals[i];
00252                      j = i;
00253                      while ( j>0 ) {
00254                             int cmp = strcmp( nvals[j-1].bv_val, ntmp.bv_val );
00255                             cmp = (sort & VALSORT_DESCEND) ? (cmp < 0) : (cmp > 0);
00256                             if ( !cmp ) break;
00257 
00258                             nvals[j] = nvals[j-1];
00259                             if ( gotnvals ) vals[j] = vals[j-1];
00260                             j--;
00261                      }
00262                      nvals[j] = ntmp;
00263                      if ( gotnvals ) vals[j] = tmp;
00264               }
00265        }
00266 }
00267 
00268 static int
00269 valsort_response( Operation *op, SlapReply *rs )
00270 {
00271        slap_overinst *on;
00272        valsort_info *vi;
00273        Attribute *a;
00274 
00275        /* If this is not a search response, or it is a syncrepl response,
00276         * or the valsort control wants raw results, pass thru unmodified.
00277         */
00278        if ( rs->sr_type != REP_SEARCH ||
00279               ( _SCM(op->o_sync) > SLAP_CONTROL_IGNORED ) ||
00280               ( op->o_ctrlflag[valsort_cid] & SLAP_CONTROL_DATA0))
00281               return SLAP_CB_CONTINUE;
00282               
00283        on = (slap_overinst *) op->o_bd->bd_info;
00284        vi = on->on_bi.bi_private;
00285 
00286        /* And we must have something configured */
00287        if ( !vi ) return SLAP_CB_CONTINUE;
00288 
00289        /* Find a rule whose baseDN matches this entry */
00290        for (; vi; vi = vi->vi_next ) {
00291               int i, n;
00292 
00293               if ( !dnIsSuffix( &rs->sr_entry->e_nname, &vi->vi_dn ))
00294                      continue;
00295 
00296               /* Find attr that this rule affects */
00297               a = attr_find( rs->sr_entry->e_attrs, vi->vi_ad );
00298               if ( !a ) continue;
00299 
00300               if ( rs_entry2modifiable( op, rs, on )) {
00301                      a = attr_find( rs->sr_entry->e_attrs, vi->vi_ad );
00302               }
00303 
00304               n = a->a_numvals;
00305               if ( vi->vi_sort & VALSORT_WEIGHTED ) {
00306                      int j, gotnvals;
00307                      long *index = op->o_tmpalloc( n * sizeof(long), op->o_tmpmemctx );
00308 
00309                      gotnvals = (a->a_vals != a->a_nvals );
00310 
00311                      for (i=0; i<n; i++) {
00312                             char *ptr = ber_bvchr( &a->a_nvals[i], '{' );
00313                             char *end = NULL;
00314                             if ( !ptr ) {
00315                                    Debug(LDAP_DEBUG_TRACE, "weights missing from attr %s "
00316                                           "in entry %s\n", vi->vi_ad->ad_cname.bv_val,
00317                                           rs->sr_entry->e_name.bv_val, 0 );
00318                                    break;
00319                             }
00320                             index[i] = strtol( ptr+1, &end, 0 );
00321                             if ( *end != '}' ) {
00322                                    Debug(LDAP_DEBUG_TRACE, "weights misformatted "
00323                                           "in entry %s\n", 
00324                                           rs->sr_entry->e_name.bv_val, 0, 0 );
00325                                    break;
00326                             }
00327                             /* Strip out weights */
00328                             ptr = a->a_nvals[i].bv_val;
00329                             end++;
00330                             for (;*end;)
00331                                    *ptr++ = *end++;
00332                             *ptr = '\0';
00333                             a->a_nvals[i].bv_len = ptr - a->a_nvals[i].bv_val;
00334 
00335                             if ( a->a_vals != a->a_nvals ) {
00336                                    ptr = a->a_vals[i].bv_val;
00337                                    end = ber_bvchr( &a->a_vals[i], '}' );
00338                                    assert( end != NULL );
00339                                    end++;
00340                                    for (;*end;)
00341                                           *ptr++ = *end++;
00342                                    *ptr = '\0';
00343                                    a->a_vals[i].bv_len = ptr - a->a_vals[i].bv_val;
00344                             }
00345                      }
00346                      /* An attr was missing weights here, ignore it */
00347                      if ( i<n ) {
00348                             op->o_tmpfree( index, op->o_tmpmemctx );
00349                             continue;
00350                      }
00351                      /* Insertion sort */
00352                      for ( i=1; i<n; i++) {
00353                             long idx = index[i];
00354                             struct berval tmp = a->a_vals[i], ntmp;
00355                             if ( gotnvals ) ntmp = a->a_nvals[i];
00356                             j = i;
00357                             while (( j>0 ) && (index[j-1] > idx )) {
00358                                    index[j] = index[j-1];
00359                                    a->a_vals[j] = a->a_vals[j-1];
00360                                    if ( gotnvals ) a->a_nvals[j] = a->a_nvals[j-1];
00361                                    j--;
00362                             }
00363                             index[j] = idx;
00364                             a->a_vals[j] = tmp;
00365                             if ( gotnvals ) a->a_nvals[j] = ntmp;
00366                      }
00367                      /* Check for secondary sort */
00368                      if ( vi->vi_sort ^ VALSORT_WEIGHTED ) {
00369                             for ( i=0; i<n;) {
00370                                    for (j=i+1; j<n; j++) {
00371                                           if (index[i] != index[j])
00372                                                  break;
00373                                    }
00374                                    if( j-i > 1 )
00375                                           do_sort( op, a, i, j-i, vi->vi_sort );
00376                                    i = j;
00377                             }
00378                      }
00379                      op->o_tmpfree( index, op->o_tmpmemctx );
00380               } else {
00381                      do_sort( op, a, 0, n, vi->vi_sort );
00382               }
00383        }
00384        return SLAP_CB_CONTINUE;
00385 }
00386 
00387 static int
00388 valsort_add( Operation *op, SlapReply *rs )
00389 {
00390        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00391        valsort_info *vi = on->on_bi.bi_private;
00392 
00393        Attribute *a;
00394        int i;
00395        char *ptr, *end;
00396 
00397        /* See if any weighted sorting applies to this entry */
00398        for ( ;vi;vi=vi->vi_next ) {
00399               if ( !dnIsSuffix( &op->o_req_ndn, &vi->vi_dn ))
00400                      continue;
00401               if ( !(vi->vi_sort & VALSORT_WEIGHTED ))
00402                      continue;
00403               a = attr_find( op->ora_e->e_attrs, vi->vi_ad );
00404               if ( !a )
00405                      continue;
00406               for (i=0; !BER_BVISNULL( &a->a_vals[i] ); i++) {
00407                      ptr = ber_bvchr(&a->a_vals[i], '{' );
00408                      if ( !ptr ) {
00409                             Debug(LDAP_DEBUG_TRACE, "weight missing from attribute %s\n",
00410                                    vi->vi_ad->ad_cname.bv_val, 0, 0);
00411                             send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
00412                                    "weight missing from attribute" );
00413                             return rs->sr_err;
00414                      }
00415                      strtol( ptr+1, &end, 0 );
00416                      if ( *end != '}' ) {
00417                             Debug(LDAP_DEBUG_TRACE, "weight is misformatted in %s\n",
00418                                    vi->vi_ad->ad_cname.bv_val, 0, 0);
00419                             send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
00420                                    "weight is misformatted" );
00421                             return rs->sr_err;
00422                      }
00423               }
00424        }
00425        return SLAP_CB_CONTINUE;
00426 }
00427 
00428 static int
00429 valsort_modify( Operation *op, SlapReply *rs )
00430 {
00431        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00432        valsort_info *vi = on->on_bi.bi_private;
00433 
00434        Modifications *ml;
00435        int i;
00436        char *ptr, *end;
00437 
00438        /* See if any weighted sorting applies to this entry */
00439        for ( ;vi;vi=vi->vi_next ) {
00440               if ( !dnIsSuffix( &op->o_req_ndn, &vi->vi_dn ))
00441                      continue;
00442               if ( !(vi->vi_sort & VALSORT_WEIGHTED ))
00443                      continue;
00444               for (ml = op->orm_modlist; ml; ml=ml->sml_next ) {
00445                      /* Must be a Delete Attr op, so no values to consider */
00446                      if ( !ml->sml_values )
00447                             continue;
00448                      if ( ml->sml_desc == vi->vi_ad )
00449                             break;
00450               }
00451               if ( !ml )
00452                      continue;
00453               for (i=0; !BER_BVISNULL( &ml->sml_values[i] ); i++) {
00454                      ptr = ber_bvchr(&ml->sml_values[i], '{' );
00455                      if ( !ptr ) {
00456                             Debug(LDAP_DEBUG_TRACE, "weight missing from attribute %s\n",
00457                                    vi->vi_ad->ad_cname.bv_val, 0, 0);
00458                             send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
00459                                    "weight missing from attribute" );
00460                             return rs->sr_err;
00461                      }
00462                      strtol( ptr+1, &end, 0 );
00463                      if ( *end != '}' ) {
00464                             Debug(LDAP_DEBUG_TRACE, "weight is misformatted in %s\n",
00465                                    vi->vi_ad->ad_cname.bv_val, 0, 0);
00466                             send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
00467                                    "weight is misformatted" );
00468                             return rs->sr_err;
00469                      }
00470               }
00471        }
00472        return SLAP_CB_CONTINUE;
00473 }
00474 
00475 static int
00476 valsort_db_open(
00477        BackendDB *be,
00478        ConfigReply *cr
00479 )
00480 {
00481        return overlay_register_control( be, LDAP_CONTROL_VALSORT );
00482 }
00483 
00484 static int
00485 valsort_destroy(
00486        BackendDB *be,
00487        ConfigReply *cr
00488 )
00489 {
00490        slap_overinst *on = (slap_overinst *)be->bd_info;
00491        valsort_info *vi = on->on_bi.bi_private, *next;
00492 
00493 #ifdef SLAP_CONFIG_DELETE
00494        overlay_unregister_control( be, LDAP_CONTROL_VALSORT );
00495 #endif /* SLAP_CONFIG_DELETE */
00496 
00497        for (; vi; vi = next) {
00498               next = vi->vi_next;
00499               ch_free( vi->vi_dn.bv_val );
00500               ch_free( vi );
00501        }
00502 
00503        return 0;
00504 }
00505 
00506 static int
00507 valsort_parseCtrl(
00508        Operation *op,
00509        SlapReply *rs,
00510        LDAPControl *ctrl )
00511 {
00512        ber_tag_t tag;
00513        BerElementBuffer berbuf;
00514        BerElement *ber = (BerElement *)&berbuf;
00515        ber_int_t flag = 0;
00516 
00517        if ( BER_BVISNULL( &ctrl->ldctl_value )) {
00518               rs->sr_text = "valSort control value is absent";
00519               return LDAP_PROTOCOL_ERROR;
00520        }
00521 
00522        if ( BER_BVISEMPTY( &ctrl->ldctl_value )) {
00523               rs->sr_text = "valSort control value is empty";
00524               return LDAP_PROTOCOL_ERROR;
00525        }
00526 
00527        ber_init2( ber, &ctrl->ldctl_value, 0 );
00528        if (( tag = ber_scanf( ber, "{b}", &flag )) == LBER_ERROR ) {
00529               rs->sr_text = "valSort control: flag decoding error";
00530               return LDAP_PROTOCOL_ERROR;
00531        }
00532 
00533        op->o_ctrlflag[valsort_cid] = ctrl->ldctl_iscritical ?
00534               SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
00535        if ( flag )
00536               op->o_ctrlflag[valsort_cid] |= SLAP_CONTROL_DATA0;
00537 
00538        return LDAP_SUCCESS;
00539 }
00540 
00541 static slap_overinst valsort;
00542 
00543 int valsort_initialize( void )
00544 {
00545        int rc;
00546 
00547        valsort.on_bi.bi_type = "valsort";
00548        valsort.on_bi.bi_db_destroy = valsort_destroy;
00549        valsort.on_bi.bi_db_open = valsort_db_open;
00550 
00551        valsort.on_bi.bi_op_add = valsort_add;
00552        valsort.on_bi.bi_op_modify = valsort_modify;
00553 
00554        valsort.on_response = valsort_response;
00555 
00556        valsort.on_bi.bi_cf_ocs = valsort_cfocs;
00557 
00558        rc = register_supported_control( LDAP_CONTROL_VALSORT,
00559               SLAP_CTRL_SEARCH | SLAP_CTRL_HIDE, NULL, valsort_parseCtrl,
00560               &valsort_cid );
00561        if ( rc != LDAP_SUCCESS ) {
00562               Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", rc, 0, 0 );
00563               return rc;
00564        }
00565 
00566        syn_numericString = syn_find( "1.3.6.1.4.1.1466.115.121.1.36" );
00567 
00568        rc = config_register_schema( valsort_cfats, valsort_cfocs );
00569        if ( rc ) return rc;
00570 
00571        return overlay_register(&valsort);
00572 }
00573 
00574 #if SLAPD_OVER_VALSORT == SLAPD_MOD_DYNAMIC
00575 int init_module( int argc, char *argv[]) {
00576        return valsort_initialize();
00577 }
00578 #endif
00579 
00580 #endif /* SLAPD_OVER_VALSORT */