]> git.sur5r.net Git - openldap/commitdiff
Add sorted values overlay
authorHoward Chu <hyc@openldap.org>
Tue, 23 Aug 2005 08:47:38 +0000 (08:47 +0000)
committerHoward Chu <hyc@openldap.org>
Tue, 23 Aug 2005 08:47:38 +0000 (08:47 +0000)
servers/slapd/overlays/Makefile.in
servers/slapd/overlays/valsort.c [new file with mode: 0644]

index 8318ad7ae68387e25c7d2914159f48b8a87863e6..02be3d119ad00c5327097eb69e21b79e2a782bee 100644 (file)
@@ -27,7 +27,8 @@ SRCS = overlays.c \
        rwm.c rwmconf.c rwmdn.c rwmmap.c \
        syncprov.c \
        translucent.c \
-       unique.c
+       unique.c \
+       valsort.c
 OBJS = overlays.o \
        @SLAPD_STATIC_OVERLAYS@
 
@@ -97,6 +98,9 @@ translucent.la : translucent.lo
 unique.la : unique.lo
        $(LTLINK_MOD) -module -o $@ unique.lo version.lo $(LINK_LIBS)
 
+valsort.la : valsort.lo
+       $(LTLINK_MOD) -module -o $@ valsort.lo version.lo $(LINK_LIBS)
+
 install-local: $(PROGRAMS)
        @if test -n "$?" ; then \
                $(MKDIR) $(DESTDIR)$(moduledir); \
diff --git a/servers/slapd/overlays/valsort.c b/servers/slapd/overlays/valsort.c
new file mode 100644 (file)
index 0000000..211b2e8
--- /dev/null
@@ -0,0 +1,490 @@
+/* valsort.c - sort attribute values */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005 The OpenLDAP Foundation.
+ * Portions copyright 2005 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+/*
+ * This overlay sorts the values of multi-valued attributes when returning
+ * them in a search response.
+ */
+#include "portable.h"
+
+#ifdef SLAPD_OVER_VALSORT
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+
+#include "slap.h"
+#include "config.h"
+#include "lutil.h"
+
+#define        VALSORT_ASCEND  0
+#define        VALSORT_DESCEND 1
+
+#define        VALSORT_ALPHA   2
+#define        VALSORT_NUMERIC 4
+
+#define        VALSORT_WEIGHTED        8
+
+typedef struct valsort_info {
+       struct valsort_info *vi_next;
+       struct berval vi_dn;
+       AttributeDescription *vi_ad;
+       slap_mask_t vi_sort;
+} valsort_info;
+
+static ConfigDriver valsort_cf_func;
+
+static ConfigTable valsort_cfats[] = {
+       { "valsort-attr", "attribute> <dn> <sort-type", 4, 5, 0, ARG_MAGIC,
+               valsort_cf_func, "( OLcfgOvAt:5.1 NAME 'olcValSortAttr' "
+                       "DESC 'Sorting rule for attribute under given DN' "
+                       "EQUALITY caseIgnoreMatch "
+                       "SYNTAX OMsDirectoryString )", NULL, NULL },
+       { NULL }
+};
+
+static ConfigOCs valsort_cfocs[] = {
+       { "( OLcfgOvOc:5.1 "
+               "NAME 'olcValSortConfig' "
+               "DESC 'Value Sorting configuration' "
+               "SUP olcOverlayConfig "
+               "MUST olcValSortAttr )",
+                       Cft_Overlay, valsort_cfats },
+       { NULL }
+};
+
+static slap_verbmasks sorts[] = {
+       { BER_BVC("alpha-ascend"), VALSORT_ASCEND|VALSORT_ALPHA },
+       { BER_BVC("alpha-descend"), VALSORT_DESCEND|VALSORT_ALPHA },
+       { BER_BVC("numeric-ascend"), VALSORT_ASCEND|VALSORT_NUMERIC },
+       { BER_BVC("numeric-ascend"), VALSORT_DESCEND|VALSORT_NUMERIC },
+       { BER_BVC("weighted"), VALSORT_WEIGHTED },
+       { BER_BVNULL, 0 }
+};
+
+static int
+valsort_cf_func(ConfigArgs *c) {
+       slap_overinst *on = (slap_overinst *)c->bi;
+       valsort_info vitmp, *vi;
+       const char *text = NULL;
+       int i;
+       struct berval bv;
+
+       if ( c->op == SLAP_CONFIG_EMIT ) {
+               for ( vi = on->on_bi.bi_private; vi; vi = vi->vi_next ) {
+                       struct berval bv2 = BER_BVNULL, bvret;
+                       char *ptr;
+                       int len;
+                       
+                       len = vi->vi_ad->ad_cname.bv_len + 1 + vi->vi_dn.bv_len + 3;
+                       i = vi->vi_sort;
+                       if ( i & VALSORT_WEIGHTED ) {
+                               enum_to_verb( sorts, VALSORT_WEIGHTED, &bv2 );
+                               len += bv2.bv_len + 1;
+                               i ^= VALSORT_WEIGHTED;
+                       }
+                       enum_to_verb( sorts, i, &bv );
+                       len += bv.bv_len;
+                       bvret.bv_val = ch_malloc( len+1 );
+                       bvret.bv_len = len;
+
+                       ptr = lutil_strcopy( bvret.bv_val, vi->vi_ad->ad_cname.bv_val );
+                       *ptr++ = ' ';
+                       *ptr++ = '"';
+                       ptr = lutil_strcopy( ptr, vi->vi_dn.bv_val );
+                       *ptr++ = '"';
+                       *ptr++ = ' ';
+                       if ( vi->vi_sort & VALSORT_WEIGHTED ) {
+                               ptr = lutil_strcopy( ptr, bv2.bv_val );
+                               *ptr++ = ' ';
+                       }
+                       strcpy( ptr, bv.bv_val );
+                       ber_bvarray_add( &c->rvalue_vals, &bvret );
+               }
+               i = ( c->rvalue_vals != NULL ) ? 0 : 1;
+               return i;
+       } else if ( c->op == LDAP_MOD_DELETE ) {
+               if ( c->valx < 0 ) {
+                       for ( vi = on->on_bi.bi_private; vi; vi = c->be->be_private ) {
+                               on->on_bi.bi_private = vi->vi_next;
+                               ch_free( vi->vi_dn.bv_val );
+                               ch_free( vi );
+                       }
+               } else {
+                       valsort_info **prev;
+
+                       for (i=0, prev = (valsort_info **)&on->on_bi.bi_private,
+                               vi = *prev; vi && i<c->valx;
+                               prev = &vi->vi_next, vi = vi->vi_next, i++ );
+                       (*prev)->vi_next = vi->vi_next;
+                       ch_free( vi->vi_dn.bv_val );
+                       ch_free( vi );
+               }
+               return 0;
+       }
+       vitmp.vi_ad = NULL;
+       i = slap_str2ad( c->argv[1], &vitmp.vi_ad, &text );
+       if ( i ) {
+               sprintf( c->msg, "<%s> %s", c->argv[0], text );
+               Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
+                       c->log, c->msg, c->argv[1] );
+               return(1);
+       }
+       ber_str2bv( c->argv[2], 0, 0, &bv );
+       i = dnNormalize( 0, NULL, NULL, &bv, &vitmp.vi_dn, NULL );
+       if ( i ) {
+               sprintf( c->msg, "<%s> unable to normalize DN", c->argv[0] );
+               Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
+                       c->log, c->msg, c->argv[2] );
+               return(1);
+       }
+       i = verb_to_mask( c->argv[3], sorts );
+       if ( BER_BVISNULL( &sorts[i].word )) {
+               sprintf( c->msg, "<%s> unrecognized sort type", c->argv[0] );
+               Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
+                       c->log, c->msg, c->argv[3] );
+               return(1);
+       }
+       vitmp.vi_sort = sorts[i].mask;
+       if ( sorts[i].mask == VALSORT_WEIGHTED && c->argc == 5 ) {
+               i = verb_to_mask( c->argv[4], sorts );
+               if ( BER_BVISNULL( &sorts[i].word )) {
+                       sprintf( c->msg, "<%s> unrecognized sort type", c->argv[0] );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
+                               c->log, c->msg, c->argv[4] );
+                       return(1);
+               }
+               vitmp.vi_sort |= sorts[i].mask;
+       }
+       vi = ch_malloc( sizeof(valsort_info) );
+       *vi = vitmp;
+       vi->vi_next = on->on_bi.bi_private;
+       on->on_bi.bi_private = vi;
+       return 0;
+}
+
+/* Use Insertion Sort algorithm on selected values */
+static void
+do_sort( Operation *op, Attribute *a, int beg, int num, slap_mask_t sort )
+{
+       int i, j, gotnvals;
+       struct berval tmp, ntmp, *vals, *nvals;
+
+       gotnvals = (a->a_vals != a->a_nvals );
+
+       nvals = a->a_nvals + beg;
+       if ( gotnvals )
+               vals = a->a_vals + beg;
+
+       if ( sort & VALSORT_NUMERIC ) {
+               long *numbers = op->o_tmpalloc( num * sizeof(long), op->o_tmpmemctx ),
+                       idx;
+               for (i=0; i<num; i++)
+                       numbers[i] = strtol( nvals[i].bv_val, NULL, 0 );
+
+               for (i=1; i<num; i++) {
+                       idx = numbers[i];
+                       ntmp = nvals[i];
+                       if ( gotnvals ) tmp = vals[i];
+                       j = i;
+                       while ( j>0 ) {
+                               int cmp = (sort & VALSORT_DESCEND) ? numbers[j-1] < idx :
+                                       numbers[j-1] > idx;
+                               if ( !cmp ) break;
+                               numbers[j] = numbers[j-1];
+                               nvals[j] = nvals[j-1];
+                               if ( gotnvals ) vals[j] = vals[j-1];
+                               j--;
+                       }
+                       numbers[j] = idx;
+                       nvals[j] = ntmp;
+                       if ( gotnvals ) vals[j] = tmp;
+               }
+               op->o_tmpfree( numbers, op->o_tmpmemctx );
+       } else {
+               for (i=1; i<num; i++) {
+                       ntmp = nvals[i];
+                       if ( gotnvals ) tmp = vals[i];
+                       j = i;
+                       while ( j>0 ) {
+                               int cmp = strcmp( nvals[j-1].bv_val, ntmp.bv_val );
+                               cmp = (sort & VALSORT_DESCEND) ? (cmp < 0) : (cmp > 0);
+                               if ( !cmp ) break;
+
+                               nvals[j] = nvals[j-1];
+                               if ( gotnvals ) vals[j] = vals[j-1];
+                               j--;
+                       }
+                       nvals[j] = ntmp;
+                       if ( gotnvals ) vals[j] = tmp;
+               }
+       }
+}
+
+static int
+valsort_response( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on;
+       valsort_info *vi;
+       Attribute *a;
+
+       /* We only want search responses */
+       if ( rs->sr_type != REP_SEARCH ) return SLAP_CB_CONTINUE;
+
+       on = (slap_overinst *) op->o_bd->bd_info;
+       vi = on->on_bi.bi_private;
+
+       /* And we must have something configured */
+       if ( !vi ) return SLAP_CB_CONTINUE;
+
+       /* Find a rule whose baseDN matches this entry */
+       for (; vi; vi = vi->vi_next ) {
+               int i, n;
+
+               if ( !dnIsSuffix( &rs->sr_entry->e_nname, &vi->vi_dn ))
+                       continue;
+
+               /* Find attr that this rule affects */
+               a = attr_find( rs->sr_entry->e_attrs, vi->vi_ad );
+               if ( !a ) continue;
+
+               if (( rs->sr_flags & ( REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED )) !=
+                       ( REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED )) {
+                       rs->sr_entry = entry_dup( rs->sr_entry );
+                       rs->sr_flags |= REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED;
+                       a = attr_find( rs->sr_entry->e_attrs, vi->vi_ad );
+               }
+
+               /* count values */
+               for ( n = 0; !BER_BVISNULL( &a->a_vals[n] ); n++ );
+
+               if ( vi->vi_sort & VALSORT_WEIGHTED ) {
+                       int j, gotnvals;
+                       long *index = op->o_tmpalloc( n * sizeof(long), op->o_tmpmemctx );
+
+                       gotnvals = (a->a_vals != a->a_nvals );
+
+                       for (i=0; i<n; i++) {
+                               char *ptr = strchr( a->a_nvals[i].bv_val, '{' );
+                               char *end = NULL;
+                               if ( !ptr ) {
+                                       Debug(LDAP_DEBUG_TRACE, "weights missing from attr %s "
+                                               "in entry %s\n", vi->vi_ad->ad_cname.bv_val,
+                                               rs->sr_entry->e_name.bv_val, 0 );
+                                       break;
+                               }
+                               index[i] = strtol( ptr+1, &end, 0 );
+                               if ( *end != '}' ) {
+                                       Debug(LDAP_DEBUG_TRACE, "weights misformatted "
+                                               "in entry %s\n", 
+                                               rs->sr_entry->e_name.bv_val, 0, 0 );
+                                       break;
+                               }
+                               /* Strip out weights */
+                               ptr = a->a_nvals[i].bv_val;
+                               end++;
+                               for (;*end;)
+                                       *ptr++ = *end++;
+                               *ptr = '\0';
+                               a->a_nvals[i].bv_len = ptr - a->a_nvals[i].bv_val;
+
+                               if ( a->a_vals != a->a_nvals ) {
+                                       ptr = a->a_vals[i].bv_val;
+                                       end = strchr( ptr, '}' ) + 1;
+                                       for (;*end;)
+                                               *ptr++ = *end++;
+                                       *ptr = '\0';
+                                       a->a_vals[i].bv_len = ptr - a->a_vals[i].bv_val;
+                               }
+                       }
+                       /* An attr was missing weights here, ignore it */
+                       if ( i<n ) {
+                               op->o_tmpfree( index, op->o_tmpmemctx );
+                               continue;
+                       }
+                       /* Insertion sort */
+                       for ( i=1; i<n; i++) {
+                               long idx = index[i];
+                               struct berval tmp = a->a_vals[i], ntmp;
+                               if ( gotnvals ) ntmp = a->a_nvals[i];
+                               j = i;
+                               while (( j>0 ) && (index[j-1] > idx )) {
+                                       index[j] = index[j-1];
+                                       a->a_vals[j] = a->a_vals[j-1];
+                                       if ( gotnvals ) a->a_nvals[j] = a->a_nvals[j-1];
+                                       j--;
+                               }
+                               index[j] = idx;
+                               a->a_vals[j] = tmp;
+                               if ( gotnvals ) a->a_nvals[j] = ntmp;
+                       }
+                       /* Check for secondary sort */
+                       if ( vi->vi_sort ^ VALSORT_WEIGHTED ) {
+                               for ( i=0; i<n;) {
+                                       for (j=i+1; j<n; j++) {
+                                               if (index[i] != index[j])
+                                                       break;
+                                       }
+                                       if( j-i > 1 )
+                                               do_sort( op, a, i, j-i, vi->vi_sort );
+                                       i = j;
+                               }
+                       }
+                       op->o_tmpfree( index, op->o_tmpmemctx );
+               } else {
+                       do_sort( op, a, 0, n, vi->vi_sort );
+               }
+       }
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+valsort_add( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       valsort_info *vi = on->on_bi.bi_private;
+
+       Attribute *a;
+       int i;
+       char *ptr, *end;
+
+       /* See if any weighted sorting applies to this entry */
+       for ( ;vi;vi=vi->vi_next ) {
+               if ( !dnIsSuffix( &op->o_req_ndn, &vi->vi_dn ))
+                       continue;
+               if ( !(vi->vi_sort & VALSORT_WEIGHTED ))
+                       continue;
+               a = attr_find( op->ora_e->e_attrs, vi->vi_ad );
+               if ( !a )
+                       continue;
+               for (i=0; !BER_BVISNULL( &a->a_vals[i] ); i++) {
+                       ptr = strchr(a->a_vals[i].bv_val, '{' );
+                       if ( !ptr ) {
+                               Debug(LDAP_DEBUG_TRACE, "weight missing from attribute %s\n",
+                                       vi->vi_ad->ad_cname.bv_val, 0, 0);
+                               send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
+                                       "weight missing from attribute" );
+                               return rs->sr_err;
+                       }
+                       strtol( ptr+1, &end, 0 );
+                       if ( *end != '}' ) {
+                               Debug(LDAP_DEBUG_TRACE, "weight is misformatted in %s\n",
+                                       vi->vi_ad->ad_cname.bv_val, 0, 0);
+                               send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
+                                       "weight is misformatted" );
+                               return rs->sr_err;
+                       }
+               }
+       }
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+valsort_modify( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       valsort_info *vi = on->on_bi.bi_private;
+
+       Modifications *ml;
+       int i;
+       char *ptr, *end;
+
+       /* See if any weighted sorting applies to this entry */
+       for ( ;vi;vi=vi->vi_next ) {
+               if ( !dnIsSuffix( &op->o_req_ndn, &vi->vi_dn ))
+                       continue;
+               if ( !(vi->vi_sort & VALSORT_WEIGHTED ))
+                       continue;
+               for (ml = op->orm_modlist; ml; ml=ml->sml_next ) {
+                       if ( ml->sml_desc == vi->vi_ad )
+                               break;
+               }
+               if ( !ml )
+                       continue;
+               for (i=0; !BER_BVISNULL( &ml->sml_values[i] ); i++) {
+                       ptr = strchr(ml->sml_values[i].bv_val, '{' );
+                       if ( !ptr ) {
+                               Debug(LDAP_DEBUG_TRACE, "weight missing from attribute %s\n",
+                                       vi->vi_ad->ad_cname.bv_val, 0, 0);
+                               send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
+                                       "weight missing from attribute" );
+                               return rs->sr_err;
+                       }
+                       strtol( ptr+1, &end, 0 );
+                       if ( *end != '}' ) {
+                               Debug(LDAP_DEBUG_TRACE, "weight is misformatted in %s\n",
+                                       vi->vi_ad->ad_cname.bv_val, 0, 0);
+                               send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
+                                       "weight is misformatted" );
+                               return rs->sr_err;
+                       }
+               }
+       }
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+valsort_destroy(
+       BackendDB *be
+)
+{
+       slap_overinst *on = (slap_overinst *)be->bd_info;
+       valsort_info *vi = on->on_bi.bi_private, *next;
+
+       for (; vi; vi = next) {
+               next = vi->vi_next;
+               ch_free( vi->vi_dn.bv_val );
+               ch_free( vi );
+       }
+
+       return 0;
+}
+
+static slap_overinst valsort;
+
+int valsort_init()
+{
+       int i, rc;
+
+       valsort.on_bi.bi_type = "valsort";
+       valsort.on_bi.bi_db_destroy = valsort_destroy;
+
+       valsort.on_bi.bi_op_add = valsort_add;
+       valsort.on_bi.bi_op_modify = valsort_modify;
+
+       valsort.on_response = valsort_response;
+
+       valsort.on_bi.bi_cf_ocs = valsort_cfocs;
+
+       rc = config_register_schema( valsort_cfats, valsort_cfocs );
+       if ( rc ) return rc;
+
+       return overlay_register(&valsort);
+}
+
+#if SLAPD_OVER_VALSORT == SLAPD_MOD_DYNAMIC
+int init_module( int argc, char *argv[]) {
+       return valsort_init();
+}
+#endif
+
+#endif /* SLAPD_OVER_VALSORT */