summaryrefslogtreecommitdiffstats
path: root/contrib/slapd-modules/datamorph/datamorph.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/slapd-modules/datamorph/datamorph.c')
-rw-r--r--contrib/slapd-modules/datamorph/datamorph.c2091
1 files changed, 2091 insertions, 0 deletions
diff --git a/contrib/slapd-modules/datamorph/datamorph.c b/contrib/slapd-modules/datamorph/datamorph.c
new file mode 100644
index 0000000..7767586
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/datamorph.c
@@ -0,0 +1,2091 @@
+/* datamorph.c - enumerated and native integer value support */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * 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 developed in 2016 by Ondřej Kuzník for Symas Corp.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_DATAMORPH
+
+#include <inttypes.h>
+#include <ac/stdlib.h>
+
+#if defined(__linux__)
+#include <endian.h>
+
+#elif defined(sun)
+
+#define be16toh(x) BE_16(x)
+#define le16toh(x) LE_16(x)
+#define htobe16(x) BE_16(x)
+#define htole16(x) LE_16(x)
+
+#define be32toh(x) BE_32(x)
+#define le32toh(x) LE_32(x)
+#define htobe32(x) BE_32(x)
+#define htole32(x) LE_32(x)
+
+#define be64toh(x) BE_64(x)
+#define le64toh(x) LE_64(x)
+#define htobe64(x) BE_64(x)
+#define htole64(x) LE_64(x)
+
+#elif defined(__NetBSD__) || defined(__FreeBSD__)
+#include <sys/endian.h>
+
+#elif defined(__OpenBSD__)
+#include <sys/endian.h>
+
+#define be16toh(x) betoh16(x)
+#define le16toh(x) letoh16(x)
+
+#define be32toh(x) betoh32(x)
+#define le32toh(x) letoh32(x)
+
+#define be64toh(x) betoh64(x)
+#define le64toh(x) letoh64(x)
+
+#elif defined(__BYTE_ORDER__) && \
+ ( defined(__GNUC__) || defined(__clang__) )
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define be16toh(x) __builtin_bswap16(x)
+#define le16toh(x) (x)
+#define htobe16(x) __builtin_bswap16(x)
+#define htole16(x) (x)
+
+#define be32toh(x) __builtin_bswap32(x)
+#define le32toh(x) (x)
+#define htobe32(x) __builtin_bswap32(x)
+#define htole32(x) (x)
+
+#define be64toh(x) __builtin_bswap64(x)
+#define le64toh(x) (x)
+#define htobe64(x) __builtin_bswap64(x)
+#define htole64(x) (x)
+
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define be16toh(x) (x)
+#define le16toh(x) __builtin_bswap16(x)
+#define htobe16(x) (x)
+#define htole16(x) __builtin_bswap16(x)
+
+#define be32toh(x) (x)
+#define le32toh(x) __builtin_bswap32(x)
+#define htobe32(x) (x)
+#define htole32(x) __builtin_bswap32(x)
+
+#define be64toh(x) (x)
+#define le64toh(x) __builtin_bswap64(x)
+#define htobe64(x) (x)
+#define htole64(x) __builtin_bswap64(x)
+
+#else
+#error "Only support pure big and little endian at the moment"
+#endif
+
+#else
+#error "I lack the way to check my endianness and convert to/from big-endian"
+#endif
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+#include "ldap_queue.h"
+
+typedef enum datamorph_type_t {
+ DATAMORPH_UNSET,
+ DATAMORPH_ENUM,
+ DATAMORPH_INT,
+} datamorph_type;
+
+typedef enum datamorph_flags_t {
+ DATAMORPH_FLAG_SIGNED = 1 << 0,
+ DATAMORPH_FLAG_LOWER = 1 << 1,
+ DATAMORPH_FLAG_UPPER = 1 << 2,
+} datamorph_flags;
+
+typedef union datamorph_interval_bound_t {
+ int64_t i;
+ uint64_t u;
+} datamorph_interval_bound;
+
+typedef struct transformation_info_t {
+ AttributeDescription *attr;
+ datamorph_type type;
+ union {
+ struct {
+ Avlnode *to_db;
+ struct berval from_db[256];
+ } maps;
+#define ti_enum info.maps
+ struct {
+ datamorph_flags flags;
+ unsigned int size;
+ datamorph_interval_bound lower, upper;
+ } interval;
+#define ti_int info.interval
+ } info;
+} transformation_info;
+
+typedef struct datamorph_enum_mapping_t {
+ struct berval wire_value;
+ uint8_t db_value;
+ transformation_info *transformation;
+} datamorph_enum_mapping;
+
+typedef struct datamorph_info_t {
+ Avlnode *transformations;
+ transformation_info *wip_transformation;
+} datamorph_info;
+
+static int
+transformation_mapping_cmp( const void *l, const void *r )
+{
+ const datamorph_enum_mapping *left = l, *right = r;
+
+ return ber_bvcmp( &left->wire_value, &right->wire_value );
+}
+
+static int
+transformation_info_cmp( const void *l, const void *r )
+{
+ const transformation_info *left = l, *right = r;
+
+ return ( left->attr == right->attr ) ? 0 :
+ ( left->attr < right->attr ) ? -1 :
+ 1;
+}
+
+static int
+transform_to_db_format_one(
+ Operation *op,
+ transformation_info *definition,
+ struct berval *value,
+ struct berval *outval )
+{
+ switch ( definition->type ) {
+ case DATAMORPH_ENUM: {
+ datamorph_enum_mapping *mapping, needle = { .wire_value = *value };
+ struct berval db_value = { .bv_len = 1 };
+
+ mapping = ldap_avl_find( definition->ti_enum.to_db, &needle,
+ transformation_mapping_cmp );
+ if ( !mapping ) {
+ Debug( LDAP_DEBUG_ANY, "transform_to_db_format_one: "
+ "value '%s' not mapped\n",
+ value->bv_val );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ db_value.bv_val = (char *)&mapping->db_value;
+ ber_dupbv( outval, &db_value );
+ assert( outval->bv_val );
+ break;
+ }
+
+ case DATAMORPH_INT: {
+ union {
+ char s[8];
+ uint8_t be8;
+ uint16_t be16;
+ uint32_t be32;
+ uint64_t be64;
+ } buf;
+ struct berval db_value = { .bv_val = buf.s };
+ char *ptr = value->bv_val + value->bv_len;
+ uint64_t unsigned_value;
+ int64_t signed_value;
+
+ assert( definition->ti_int.size == 1 ||
+ definition->ti_int.size == 2 ||
+ definition->ti_int.size == 4 ||
+ definition->ti_int.size == 8 );
+
+ /* Read number */
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = strtoll( value->bv_val, &ptr, 10 );
+ } else {
+ unsigned_value = strtoull( value->bv_val, &ptr, 10 );
+ }
+ if ( *value->bv_val == '\0' || *ptr != '\0' ) {
+ Debug( LDAP_DEBUG_ANY, "transform_to_db_format_one: "
+ "value '%s' not an integer\n",
+ value->bv_val );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ /* Check it's within configured bounds */
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ if ( signed_value < definition->ti_int.lower.i ||
+ signed_value > definition->ti_int.upper.i ) {
+ Debug( LDAP_DEBUG_ANY, "transform_to_db_format_one: "
+ "value '%s' doesn't fit configured constraints\n",
+ value->bv_val );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ } else {
+ if ( unsigned_value < definition->ti_int.lower.u ||
+ unsigned_value > definition->ti_int.upper.u ) {
+ Debug( LDAP_DEBUG_ANY, "transform_to_db_format_one: "
+ "value '%s' doesn't fit configured constraints\n",
+ value->bv_val );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ }
+
+ db_value.bv_len = definition->ti_int.size;
+ switch ( definition->ti_int.size ) {
+ case 1: {
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ buf.be8 = (unsigned char)((char)signed_value);
+ } else {
+ buf.be8 = unsigned_value;
+ }
+ break;
+ }
+ case 2: {
+ uint16_t h16;
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ h16 = signed_value;
+ } else {
+ h16 = unsigned_value;
+ }
+ buf.be16 = htobe16( h16 );
+ break;
+ }
+ case 4: {
+ uint32_t h32;
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ h32 = signed_value;
+ } else {
+ h32 = unsigned_value;
+ }
+ buf.be32 = htobe32( h32 );
+ break;
+ }
+ case 8: {
+ uint64_t h64;
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ h64 = signed_value;
+ } else {
+ h64 = unsigned_value;
+ }
+ buf.be64 = htobe64( h64 );
+ break;
+ }
+ }
+ ber_dupbv( outval, &db_value );
+ assert( outval->bv_val );
+ break;
+ }
+
+ default:
+ assert(0);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+transform_to_db_format(
+ Operation *op,
+ transformation_info *definition,
+ BerVarray values,
+ int numvals,
+ BerVarray *out )
+{
+ struct berval *value;
+ int i, rc = LDAP_SUCCESS;
+
+ if ( numvals == 0 ) {
+ for ( value = values; value; value++, numvals++ )
+ ; /* Count them */
+ }
+
+ assert( out );
+ *out = ch_calloc( numvals + 1, sizeof(struct berval) );
+
+ for ( i = 0; i < numvals; i++ ) {
+ rc = transform_to_db_format_one(
+ op, definition, &values[i], &(*out)[i] );
+ if ( rc ) {
+ break;
+ }
+ }
+
+ if ( rc ) {
+ for ( ; i >= 0; i-- ) {
+ ch_free((*out)[i].bv_val);
+ }
+ ch_free(*out);
+ }
+
+ return rc;
+}
+
+static int
+transform_from_db_format_one(
+ Operation *op,
+ transformation_info *definition,
+ struct berval *value,
+ struct berval *outval )
+{
+ switch ( definition->type ) {
+ case DATAMORPH_ENUM: {
+ uint8_t index = value->bv_val[0];
+ struct berval *val = &definition->info.maps.from_db[index];
+
+ if ( !BER_BVISNULL( val ) ) {
+ ber_dupbv( outval, val );
+ assert( outval->bv_val );
+ } else {
+ Debug( LDAP_DEBUG_ANY, "transform_from_db_format_one: "
+ "DB value %d has no mapping!\n",
+ index );
+ /* FIXME: probably still need to return an error */
+ BER_BVZERO( outval );
+ }
+ break;
+ }
+
+ case DATAMORPH_INT: {
+ char buf[24];
+ struct berval wire_value = { .bv_val = buf };
+ union lens_t {
+ uint8_t be8;
+ uint16_t be16;
+ uint32_t be32;
+ uint64_t be64;
+ } *lens = (union lens_t *)value->bv_val;
+ uint64_t unsigned_value;
+ int64_t signed_value;
+
+ if ( value->bv_len != definition->ti_int.size ) {
+ Debug( LDAP_DEBUG_ANY, "transform_from_db_format_one(%s): "
+ "unexpected DB value of length %lu when configured "
+ "for %u!\n",
+ definition->attr->ad_cname.bv_val, value->bv_len,
+ definition->ti_int.size );
+ /* FIXME: probably still need to return an error */
+ BER_BVZERO( outval );
+ break;
+ }
+
+ assert( definition->ti_int.size == 1 ||
+ definition->ti_int.size == 2 ||
+ definition->ti_int.size == 4 ||
+ definition->ti_int.size == 8 );
+
+ switch ( definition->ti_int.size ) {
+ case 1: {
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = (int8_t)lens->be8;
+ } else {
+ unsigned_value = (uint8_t)lens->be8;
+ }
+ break;
+ }
+ case 2: {
+ uint16_t h16 = be16toh( lens->be16 );
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = (int16_t)h16;
+ } else {
+ unsigned_value = (uint16_t)h16;
+ }
+ break;
+ }
+ case 4: {
+ uint32_t h32 = be32toh( lens->be32 );
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = (int32_t)h32;
+ } else {
+ unsigned_value = (uint32_t)h32;
+ }
+ break;
+ }
+ case 8: {
+ uint64_t h64 = be64toh( lens->be64 );
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = (int64_t)h64;
+ } else {
+ unsigned_value = (uint64_t)h64;
+ }
+ break;
+ }
+ }
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ wire_value.bv_len = sprintf( buf, "%" PRId64, signed_value );
+ } else {
+ wire_value.bv_len = sprintf( buf, "%" PRIu64, unsigned_value );
+ }
+ ber_dupbv( outval, &wire_value );
+ assert( outval->bv_val );
+ break;
+ }
+
+ default:
+ assert(0);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+transform_from_db_format(
+ Operation *op,
+ transformation_info *definition,
+ BerVarray values,
+ int numvals,
+ BerVarray *out )
+{
+ struct berval *value;
+ int i, rc = LDAP_SUCCESS;
+
+ if ( numvals == 0 ) {
+ for ( value = values; value; value++, numvals++ )
+ ; /* Count them */
+ }
+
+ assert( out );
+ *out = ch_calloc( numvals + 1, sizeof(struct berval) );
+
+ for ( i = 0; i < numvals; i++ ) {
+ struct berval bv;
+ rc = transform_from_db_format_one( op, definition, &values[i], &bv );
+ if ( !BER_BVISNULL( &bv ) ) {
+ ber_bvarray_add( out, &bv );
+ }
+ if ( rc ) {
+ break;
+ }
+ }
+
+ if ( rc ) {
+ for ( ; i >= 0; i-- ) {
+ ch_free( (*out)[i].bv_val );
+ }
+ ch_free( *out );
+ }
+
+ return rc;
+}
+
+static int
+datamorph_filter( Operation *op, datamorph_info *ov, Filter *f )
+{
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_PRESENT:
+ /* The matching rules are not in place,
+ * so the filter will be ignored */
+ case LDAP_FILTER_APPROX:
+ case LDAP_FILTER_SUBSTRINGS:
+ default:
+ break;
+ return LDAP_SUCCESS;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR: {
+ for ( f = f->f_and; f; f = f->f_next ) {
+ int rc = datamorph_filter( op, ov, f );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+ } break;
+
+ case LDAP_FILTER_NOT:
+ return datamorph_filter( op, ov, f->f_not );
+
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE: {
+ transformation_info *t, needle = { .attr = f->f_ava->aa_desc };
+
+ t = ldap_avl_find(
+ ov->transformations, &needle, transformation_info_cmp );
+ if ( t ) {
+ struct berval new_val;
+ int rc = transform_to_db_format_one(
+ op, t, &f->f_ava->aa_value, &new_val );
+ ch_free( f->f_ava->aa_value.bv_val );
+
+ if ( rc != LDAP_SUCCESS ) {
+ f->f_choice = SLAPD_FILTER_COMPUTED;
+ f->f_result = SLAPD_COMPARE_UNDEFINED;
+ } else {
+ f->f_ava->aa_value = new_val;
+ }
+ }
+ } break;
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_op_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ Entry *e = op->ora_e;
+ Attribute *a, *next;
+ AttributeDescription *stop = NULL;
+ int rc = LDAP_SUCCESS;
+
+ if ( !BER_BVISNULL( &e->e_nname ) && !BER_BVISEMPTY( &e->e_nname ) ) {
+ LDAPRDN rDN;
+ const char *p;
+ int i;
+
+ rc = ldap_bv2rdn_x( &e->e_nname, &rDN, (char **)&p, LDAP_DN_FORMAT_LDAP,
+ op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph_op_add: "
+ "can't parse rdn: dn=%s\n",
+ op->o_req_ndn.bv_val );
+ return SLAP_CB_CONTINUE;
+ }
+
+ for ( i = 0; rDN[i]; i++ ) {
+ transformation_info needle = {};
+
+ /* If we can't resolve the attribute, ignore it */
+ if ( slap_bv2ad( &rDN[i]->la_attr, &needle.attr, &p ) ) {
+ continue;
+ }
+
+ if ( ldap_avl_find( ov->transformations, &needle,
+ transformation_info_cmp ) ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ Debug( LDAP_DEBUG_TRACE, "datamorph_op_add: "
+ "attempted to add transformed attribute in RDN\n" );
+ break;
+ }
+ }
+
+ ldap_rdnfree_x( rDN, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_error( op, rs, rc,
+ "datamorph: trying to add transformed attribute in RDN" );
+ return rc;
+ }
+ }
+
+ for ( a = e->e_attrs; a && a->a_desc != stop; a = next ) {
+ transformation_info *t, needle = { .attr = a->a_desc };
+ BerVarray new_vals;
+
+ next = a->a_next;
+
+ t = ldap_avl_find( ov->transformations, &needle, transformation_info_cmp );
+ if ( !t ) continue;
+
+ rc = transform_to_db_format(
+ op, t, a->a_vals, a->a_numvals, &new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+
+ (void)attr_delete( &e->e_attrs, needle.attr );
+
+ rc = attr_merge( e, needle.attr, new_vals, NULL );
+ ber_bvarray_free( new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+ if ( !stop ) {
+ stop = needle.attr;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+
+fail:
+ send_ldap_error(
+ op, rs, rc, "datamorph: trying to add values outside definitions" );
+ return rc;
+}
+
+static int
+datamorph_op_compare( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ transformation_info *t, needle = { .attr = op->orc_ava->aa_desc };
+ int rc = SLAP_CB_CONTINUE;
+
+ t = ldap_avl_find( ov->transformations, &needle, transformation_info_cmp );
+ if ( t ) {
+ struct berval new_val;
+
+ rc = transform_to_db_format_one(
+ op, t, &op->orc_ava->aa_value, &new_val );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "datamorph_op_compare: "
+ "transformation failed for '%s', rc=%d\n",
+ op->orc_ava->aa_value.bv_val, rc );
+ rs->sr_err = rc = LDAP_COMPARE_FALSE;
+ send_ldap_result( op, rs );
+ return rc;
+ }
+ ch_free( op->orc_ava->aa_value.bv_val );
+ op->orc_ava->aa_value = new_val;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+datamorph_op_mod( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ Modifications *mod;
+ int rc = SLAP_CB_CONTINUE;
+
+ for ( mod = op->orm_modlist; mod; mod = mod->sml_next ) {
+ transformation_info *t, needle = { .attr = mod->sml_desc };
+ BerVarray new_vals = NULL;
+
+ if ( mod->sml_numvals == 0 ) continue; /* Nothing to transform */
+
+ t = ldap_avl_find( ov->transformations, &needle, transformation_info_cmp );
+ if ( !t ) continue;
+
+ assert( !mod->sml_nvalues );
+ rc = transform_to_db_format(
+ op, t, mod->sml_values, mod->sml_numvals, &new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+ ber_bvarray_free( mod->sml_values );
+ mod->sml_values = new_vals;
+ }
+
+ return SLAP_CB_CONTINUE;
+
+fail:
+ Debug( LDAP_DEBUG_TRACE, "datamorph_op_mod: "
+ "dn=%s failed rc=%d\n",
+ op->o_req_ndn.bv_val, rc );
+ send_ldap_error( op, rs, rc,
+ "datamorph: trying to operate on values outside definitions" );
+ return rc;
+}
+
+static int
+datamorph_op_modrdn( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ LDAPRDN rDN;
+ const char *p;
+ int i, rc;
+
+ rc = ldap_bv2rdn_x( &op->orr_nnewrdn, &rDN, (char **)&p,
+ LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph_op_modrdn: "
+ "can't parse rdn for dn=%s\n",
+ op->o_req_ndn.bv_val );
+ return SLAP_CB_CONTINUE;
+ }
+
+ for ( i = 0; rDN[i]; i++ ) {
+ transformation_info needle = {};
+
+ /* If we can't resolve the attribute, ignore it */
+ if ( slap_bv2ad( &rDN[i]->la_attr, &needle.attr, &p ) ) {
+ continue;
+ }
+
+ if ( ldap_avl_find(
+ ov->transformations, &needle, transformation_info_cmp ) ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ Debug( LDAP_DEBUG_TRACE, "datamorph_op_modrdn: "
+ "attempted to add transformed values in RDN\n" );
+ break;
+ }
+ }
+
+ ldap_rdnfree_x( rDN, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_error( op, rs, rc,
+ "datamorph: trying to put transformed values in RDN" );
+ return rc;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+datamorph_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ Entry *e = NULL, *e_orig = rs->sr_entry;
+ AttributeDescription *stop = NULL;
+ Attribute *a, *next = NULL;
+ int rc = SLAP_CB_CONTINUE;
+
+ if ( rs->sr_type != REP_SEARCH ) {
+ return rc;
+ }
+
+ for ( a = e_orig->e_attrs; a && a->a_desc != stop; a = next ) {
+ transformation_info *t, needle = { .attr = a->a_desc };
+ BerVarray new_vals;
+
+ next = a->a_next;
+
+ t = ldap_avl_find( ov->transformations, &needle, transformation_info_cmp );
+ if ( !t ) continue;
+
+ rc = transform_from_db_format(
+ op, t, a->a_vals, a->a_numvals, &new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ if ( !e ) {
+ if ( rs->sr_flags & REP_ENTRY_MODIFIABLE ) {
+ e = e_orig;
+ } else {
+ e = entry_dup( e_orig );
+ }
+ }
+
+ (void)attr_delete( &e->e_attrs, needle.attr );
+
+ rc = attr_merge( e, needle.attr, new_vals, NULL );
+ ber_bvarray_free( new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ if ( !stop ) {
+ stop = needle.attr;
+ }
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = SLAP_CB_CONTINUE;
+ if ( e && e != e_orig ) {
+ rs_replace_entry( op, rs, on, e );
+ rs->sr_flags &= ~REP_ENTRY_MASK;
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
+ }
+ } else if ( e && e != e_orig ) {
+ entry_free( e );
+ }
+
+ return rc;
+}
+
+static int
+datamorph_op_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ int rc = SLAP_CB_CONTINUE;
+
+ /*
+ * 1. check all requested attributes -> register callback if one matches
+ * 2. check filter: parse filter, traverse, for configured attributes:
+ * - presence -> do not touch
+ * - ava -> replace assertion value with db value if possible, assertion with undefined otherwise
+ * - inequality -> ???
+ * - anything else -> undefined
+ * - might just check for equality and leave the rest to syntax?
+ * 3. unparse filter
+ */
+ if ( datamorph_filter( op, ov, op->ors_filter ) ) {
+ send_ldap_error(
+ op, rs, LDAP_OTHER, "datamorph: failed to process filter" );
+ return LDAP_OTHER;
+ }
+
+ return rc;
+}
+
+static int
+datamorph_entry_release_rw( Operation *op, Entry *e, int rw )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ int rc = LDAP_SUCCESS;
+
+ if ( on->on_next ) {
+ rc = overlay_entry_release_ov( op, e, rw, on->on_next );
+ } else if ( on->on_info->oi_orig->bi_entry_release_rw ) {
+ /* FIXME: there should be a better way */
+ rc = on->on_info->oi_orig->bi_entry_release_rw( op, e, rw );
+ } else {
+ entry_free( e );
+ }
+
+ return rc;
+}
+
+static int
+datamorph_entry_get_rw(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **ep )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ Entry *e_orig, *e = NULL;
+ int rc;
+
+ if ( on->on_next ) {
+ rc = overlay_entry_get_ov( op, ndn, oc, at, rw, ep, on->on_next );
+ } else {
+ /* FIXME: there should be a better way */
+ rc = on->on_info->oi_orig->bi_entry_get_rw( op, ndn, oc, at, rw, ep );
+ }
+ e_orig = *ep;
+
+ if ( rc == LDAP_SUCCESS && e_orig ) {
+ AttributeDescription *stop = NULL;
+ Attribute *a;
+
+ for ( a = e_orig->e_attrs; a; a = a->a_next ) {
+ transformation_info *t, needle = { .attr = a->a_desc };
+ BerVarray new_vals;
+
+ t = ldap_avl_find(
+ ov->transformations, &needle, transformation_info_cmp );
+ if ( !t ) continue;
+
+ rc = transform_from_db_format(
+ op, t, a->a_vals, a->a_numvals, &new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+ if ( !e ) {
+ e = entry_dup( e_orig );
+ }
+
+ (void)attr_delete( &e->e_attrs, needle.attr );
+
+ rc = attr_merge( e, needle.attr, new_vals, NULL );
+ ber_bvarray_free( new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+ if ( !stop ) {
+ stop = needle.attr;
+ }
+ }
+ }
+ if ( e ) {
+ datamorph_entry_release_rw( op, e_orig, rw );
+ *ep = e;
+ }
+
+ return rc;
+
+fail:
+ if ( e ) {
+ entry_free( e );
+ }
+ (void)datamorph_entry_release_rw( op, *ep, rw );
+ return rc;
+}
+
+/* Schema */
+
+static int
+datamorphBlobValidate( Syntax *syntax, struct berval *in )
+{
+ /* any value allowed */
+ return LDAP_SUCCESS;
+}
+
+int
+datamorphBinarySignedOrderingMatch( int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ struct berval *asserted = assertedValue;
+ ber_len_t v_len = value->bv_len;
+ ber_len_t av_len = asserted->bv_len;
+
+ /* Ordering:
+ * 1. Negative always before non-negative
+ * 2. Shorter before longer
+ * 3. Rest ordered by memory contents (they are big-endian numbers)
+ */
+ int match = ( *value->bv_val >= 0 ) - ( *asserted->bv_val >= 0 );
+
+ if ( match == 0 ) match = (int)v_len - (int)av_len;
+
+ if ( match == 0 ) match = memcmp( value->bv_val, asserted->bv_val, v_len );
+
+ /* If used in extensible match filter, match if value < asserted */
+ if ( flags & SLAP_MR_EXT ) match = ( match >= 0 );
+
+ *matchp = match;
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Ordered index */
+int
+datamorphUnsignedIndexer( slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp,
+ void *ctx )
+{
+ int i;
+ BerVarray keys;
+
+ for ( i = 0; values[i].bv_val != NULL; i++ ) {
+ /* just count them */
+ }
+
+ /* we should have at least one value at this point */
+ assert( i > 0 );
+
+ keys = slap_sl_malloc( sizeof(struct berval) * ( i + 1 ), ctx );
+
+ for ( i = 0; values[i].bv_val != NULL; i++ ) {
+ ber_dupbv_x( &keys[i], &values[i], ctx );
+ }
+
+ BER_BVZERO( &keys[i] );
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Ordered index */
+int
+datamorphUnsignedFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void *assertedValue,
+ BerVarray *keysp,
+ void *ctx )
+{
+ BerVarray keys;
+ BerValue *value = assertedValue;
+
+ keys = slap_sl_malloc( sizeof(struct berval) * 2, ctx );
+ ber_dupbv_x( &keys[0], value, ctx );
+
+ BER_BVZERO( &keys[1] );
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Ordered index */
+int
+datamorphSignedIndexer(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp,
+ void *ctx )
+{
+ int i;
+ BerVarray keys;
+
+ for ( i = 0; values[i].bv_val != NULL; i++ ) {
+ /* just count them */
+ }
+
+ /* we should have at least one value at this point */
+ assert( i > 0 );
+
+ keys = slap_sl_malloc( sizeof(struct berval) * ( i + 1 ), ctx );
+
+ for ( i = 0; values[i].bv_val != NULL; i++ ) {
+ keys[i].bv_len = values[i].bv_len + 1;
+ keys[i].bv_val = slap_sl_malloc( keys[i].bv_len, ctx );
+
+ /* if positive (highest bit is not set), note that in the first byte */
+ *keys[i].bv_val = ~( *values[i].bv_val & 0x80 );
+ AC_MEMCPY( keys[i].bv_val + 1, values[i].bv_val, values[i].bv_len );
+ }
+
+ BER_BVZERO( &keys[i] );
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Ordered index */
+int
+datamorphSignedFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void *assertedValue,
+ BerVarray *keysp,
+ void *ctx )
+{
+ BerVarray keys;
+ BerValue *value = assertedValue;
+
+ keys = slap_sl_malloc( sizeof(struct berval) * 2, ctx );
+
+ keys[0].bv_len = value->bv_len + 1;
+ keys[0].bv_val = slap_sl_malloc( keys[0].bv_len, ctx );
+
+ /* if positive (highest bit is not set), note that in the first byte */
+ *keys[0].bv_val = ~( *value->bv_val & 0x80 );
+ AC_MEMCPY( keys[0].bv_val + 1, value->bv_val, value->bv_len );
+
+ BER_BVZERO( &keys[1] );
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+#define DATAMORPH_ARC "1.3.6.1.4.1.4203.666.11.12"
+
+#define DATAMORPH_SYNTAXES DATAMORPH_ARC ".1"
+#define DATAMORPH_SYNTAX_BASE DATAMORPH_SYNTAXES ".1"
+#define DATAMORPH_SYNTAX_ENUM DATAMORPH_SYNTAXES ".2"
+#define DATAMORPH_SYNTAX_INT DATAMORPH_SYNTAXES ".3"
+#define DATAMORPH_SYNTAX_SIGNED_INT DATAMORPH_SYNTAXES ".4"
+
+#define DATAMORPH_MATCHES DATAMORPH_ARC ".2"
+#define DATAMORPH_MATCH_EQUALITY DATAMORPH_MATCHES ".1"
+#define DATAMORPH_MATCH_SIGNED_EQUALITY DATAMORPH_MATCHES ".2"
+#define DATAMORPH_MATCH_ORDERING DATAMORPH_MATCHES ".3"
+#define DATAMORPH_MATCH_SIGNED_ORDERING DATAMORPH_MATCHES ".4"
+
+static char *datamorph_sups[] = {
+ DATAMORPH_SYNTAX_BASE,
+ NULL
+};
+
+static char *datamorphSyntaxes[] = {
+ DATAMORPH_SYNTAX_SIGNED_INT,
+ DATAMORPH_SYNTAX_ENUM,
+ DATAMORPH_SYNTAX_INT,
+
+ NULL
+};
+
+static slap_syntax_defs_rec datamorph_syntax_defs[] = {
+ { "( " DATAMORPH_SYNTAX_BASE " DESC 'Fixed size value' )",
+ 0, NULL, NULL, NULL
+ },
+ { "( " DATAMORPH_SYNTAX_ENUM " DESC 'Enumerated value' )",
+ 0, datamorph_sups, datamorphBlobValidate, NULL
+ },
+ { "( " DATAMORPH_SYNTAX_INT " DESC 'Fixed-size integer' )",
+ 0, datamorph_sups, datamorphBlobValidate, NULL
+ },
+ { "( " DATAMORPH_SYNTAX_SIGNED_INT " DESC 'Fixed-size signed integer' )",
+ 0, datamorph_sups, datamorphBlobValidate, NULL
+ },
+
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+static Syntax *datamorph_base_syntax;
+
+static slap_mrule_defs_rec datamorph_mrule_defs[] = {
+ { "( " DATAMORPH_MATCH_EQUALITY
+ " NAME 'fixedSizeIntegerMatch'"
+ " SYNTAX " DATAMORPH_SYNTAX_INT " )",
+ SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_ORDERED_INDEX,
+ datamorphSyntaxes + 1,
+ NULL, NULL, octetStringOrderingMatch,
+ datamorphUnsignedIndexer, datamorphUnsignedFilter,
+ NULL
+ },
+
+ { "( " DATAMORPH_MATCH_SIGNED_EQUALITY
+ " NAME 'fixedSizeSignedIntegerMatch'"
+ " SYNTAX " DATAMORPH_SYNTAX_SIGNED_INT " )",
+ SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_ORDERED_INDEX,
+ NULL,
+ NULL, NULL, datamorphBinarySignedOrderingMatch,
+ datamorphSignedIndexer, datamorphSignedFilter,
+ NULL
+ },
+
+ { "( " DATAMORPH_MATCH_ORDERING
+ " NAME 'fixedSizeIntegerOrderingMatch'"
+ " SYNTAX " DATAMORPH_SYNTAX_INT " )",
+ SLAP_MR_ORDERING|SLAP_MR_EXT|SLAP_MR_ORDERED_INDEX,
+ datamorphSyntaxes + 1,
+ NULL, NULL, octetStringOrderingMatch,
+ datamorphUnsignedIndexer, datamorphUnsignedFilter,
+ "octetStringMatch" },
+
+ { "( " DATAMORPH_MATCH_SIGNED_ORDERING
+ " NAME 'fixedSizeSignedIntegerOrderingMatch'"
+ " SYNTAX " DATAMORPH_SYNTAX_SIGNED_INT " )",
+ SLAP_MR_ORDERING|SLAP_MR_EXT|SLAP_MR_ORDERED_INDEX,
+ NULL,
+ NULL, NULL, datamorphBinarySignedOrderingMatch,
+ datamorphSignedIndexer, datamorphSignedFilter,
+ "octetStringMatch" },
+
+ { NULL, SLAP_MR_NONE, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+/* Configuration */
+
+static ConfigLDAPadd datamorph_ldadd_enum;
+static ConfigLDAPadd datamorph_ldadd_interval;
+static ConfigLDAPadd datamorph_ldadd_mapping;
+
+static ConfigDriver datamorph_set_attribute;
+static ConfigDriver datamorph_set_size;
+static ConfigDriver datamorph_set_signed;
+static ConfigDriver datamorph_set_bounds;
+static ConfigDriver datamorph_set_index;
+static ConfigDriver datamorph_set_value;
+static ConfigDriver datamorph_add_mapping;
+static ConfigDriver datamorph_add_transformation;
+
+static ConfigCfAdd datamorph_cfadd;
+
+enum {
+ DATAMORPH_INT_SIZE = 1,
+ DATAMORPH_INT_SIGNED,
+ DATAMORPH_INT_LOWER,
+ DATAMORPH_INT_UPPER,
+
+ DATAMORPH_INT_LAST,
+};
+
+static ConfigTable datamorph_cfg[] = {
+ { "datamorph_attribute", "attr", 2, 2, 0,
+ ARG_STRING|ARG_QUOTE|ARG_MAGIC,
+ datamorph_set_attribute,
+ "( OLcfgCtAt:7.1 NAME 'olcDatamorphAttribute' "
+ "DESC 'Attribute to transform' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "datamorph_size", "<1|2|4|8>", 2, 2, 0,
+ ARG_INT|ARG_MAGIC|DATAMORPH_INT_SIZE,
+ datamorph_set_size,
+ "( OLcfgCtAt:7.2 NAME 'olcDatamorphIntegerBytes' "
+ "DESC 'Integer size in bytes' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "datamorph_signed", "TRUE|FALSE", 2, 2, 0,
+ ARG_ON_OFF|ARG_MAGIC|DATAMORPH_INT_SIGNED,
+ datamorph_set_signed,
+ "( OLcfgCtAt:7.3 NAME 'olcDatamorphIntegerSigned' "
+ "DESC 'Whether integers maintain sign' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "datamorph_lower_bound", "int", 2, 2, 0,
+ ARG_BERVAL|ARG_MAGIC|DATAMORPH_INT_LOWER,
+ datamorph_set_bounds,
+ "( OLcfgCtAt:7.4 NAME 'olcDatamorphIntegerLowerBound' "
+ "DESC 'Lowest valid value for the attribute' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "datamorph_upper_bound", "int", 2, 2, 0,
+ ARG_BERVAL|ARG_MAGIC|DATAMORPH_INT_UPPER,
+ datamorph_set_bounds,
+ "( OLcfgCtAt:7.5 NAME 'olcDatamorphIntegerUpperBound' "
+ "DESC 'Highest valid value for the attribute' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+
+ /* These have no equivalent in slapd.conf */
+ { "", NULL, 2, 2, 0,
+ ARG_INT|ARG_MAGIC,
+ datamorph_set_index,
+ "( OLcfgCtAt:7.6 NAME 'olcDatamorphIndex' "
+ "DESC 'Internal DB value' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_BERVAL|ARG_QUOTE|ARG_MAGIC,
+ datamorph_set_value,
+ "( OLcfgCtAt:7.7 NAME 'olcDatamorphValue' "
+ "DESC 'Wire value' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+
+ /* slapd.conf alternative for the two above */
+ { "datamorph_value", "int> <name", 3, 3, 0,
+ ARG_QUOTE|ARG_MAGIC,
+ datamorph_add_mapping,
+ NULL, NULL, NULL
+ },
+
+ /* slapd.conf alternative for objectclasses below */
+ { "datamorph", "enum|int> <attr", 3, 3, 0,
+ ARG_QUOTE|ARG_MAGIC,
+ datamorph_add_transformation,
+ NULL, NULL, NULL
+ },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs datamorph_ocs[] = {
+ { "( OLcfgCtOc:7.1 "
+ "NAME 'olcDatamorphConfig' "
+ "DESC 'Datamorph overlay configuration' "
+ "SUP olcOverlayConfig )",
+ Cft_Overlay, datamorph_cfg, NULL, datamorph_cfadd },
+ { "( OLcfgCtOc:7.2 "
+ "NAME 'olcTransformation' "
+ "DESC 'Transformation configuration' "
+ "MUST ( olcDatamorphAttribute ) "
+ "SUP top "
+ "ABSTRACT )",
+ Cft_Misc, datamorph_cfg, NULL },
+ { "( OLcfgCtOc:7.3 "
+ "NAME 'olcDatamorphEnum' "
+ "DESC 'Configuration for an enumerated attribute' "
+ "SUP olcTransformation "
+ "STRUCTURAL )",
+ Cft_Misc, datamorph_cfg, datamorph_ldadd_enum },
+ { "( OLcfgCtOc:7.4 "
+ "NAME 'olcDatamorphInteger' "
+ "DESC 'Configuration for a compact integer attribute' "
+ "MUST ( olcDatamorphIntegerBytes ) "
+ "MAY ( olcDatamorphIntegerLowerBound $ "
+ "olcDatamorphIntegerUpperBound $ "
+ "olcDatamorphIntegerSigned "
+ ") "
+ "SUP olcTransformation "
+ "STRUCTURAL )",
+ Cft_Misc, datamorph_cfg, datamorph_ldadd_interval },
+ { "( OLcfgCtOc:7.5 "
+ "NAME 'olcDatamorphEnumValue' "
+ "DESC 'Configuration for an enumerated attribute' "
+ "MUST ( olcDatamorphIndex $ "
+ "olcDatamorphValue "
+ ") "
+ "STRUCTURAL )",
+ Cft_Misc, datamorph_cfg, datamorph_ldadd_mapping },
+
+ { NULL, 0, NULL }
+};
+
+static void
+datamorph_mapping_free( void *arg )
+{
+ datamorph_enum_mapping *mapping = arg;
+
+ ch_free( mapping->wire_value.bv_val );
+ ch_free( mapping );
+}
+
+static void
+datamorph_info_free( void *arg )
+{
+ transformation_info *info = arg;
+
+ if ( info->type == DATAMORPH_ENUM ) {
+ ldap_avl_free( info->ti_enum.to_db, datamorph_mapping_free );
+ }
+ ch_free( info );
+}
+
+static int
+datamorph_set_attribute( ConfigArgs *ca )
+{
+ transformation_info needle = {}, *info = ca->ca_private;
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ char *s = ca->value_string;
+ const char *text;
+ int rc = LDAP_SUCCESS;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_string = info->attr->ad_cname.bv_val;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ info = ldap_avl_delete( &ov->transformations, info,
+ transformation_info_cmp );
+ assert( info );
+
+ info->attr = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ if ( *s == '{' ) {
+ s = strchr( s, '}' );
+ if ( !s ) {
+ rc = LDAP_UNDEFINED_TYPE;
+ goto done;
+ }
+ s += 1;
+ }
+
+ rc = slap_str2ad( s, &info->attr, &text );
+ ch_free( ca->value_string );
+ if ( rc ) {
+ goto done;
+ }
+
+ /* The type has to be set appropriately */
+ if ( !info->attr->ad_type->sat_syntax->ssyn_sups ||
+ info->attr->ad_type->sat_syntax->ssyn_sups[0] !=
+ datamorph_base_syntax ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "improper syntax for attribute %s",
+ info->attr->ad_cname.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+ needle.attr = info->attr;
+ if ( ldap_avl_find( ov->transformations, &needle, transformation_info_cmp ) ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+done:
+ if ( rc ) {
+ ca->reply.err = rc;
+ }
+ return rc;
+}
+
+static int
+datamorph_set_size( ConfigArgs *ca )
+{
+ transformation_info *info = ca->ca_private;
+
+ if ( !info ) {
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ info = ov->wip_transformation;
+ assert( ca->op == SLAP_CONFIG_ADD );
+ }
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_int = info->ti_int.size;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ info->ti_int.size = 0;
+ return LDAP_SUCCESS;
+ }
+
+ if ( ca->value_int != 1 &&
+ ca->value_int != 2 &&
+ ca->value_int != 4 &&
+ ca->value_int != 8 ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg), "invalid size %d",
+ ca->value_int );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ info->ti_int.size = ca->value_int;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_set_signed( ConfigArgs *ca )
+{
+ transformation_info *info = ca->ca_private;
+
+ if ( !info ) {
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ info = ov->wip_transformation;
+ assert( ca->op == SLAP_CONFIG_ADD );
+ }
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_int = info->ti_int.flags & DATAMORPH_FLAG_SIGNED;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ info->ti_int.flags &= ~DATAMORPH_FLAG_SIGNED;
+ return LDAP_SUCCESS;
+ }
+
+ info->ti_int.flags &= ~DATAMORPH_FLAG_SIGNED;
+ if ( ca->value_int ) {
+ info->ti_int.flags |= DATAMORPH_FLAG_SIGNED;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_set_bounds( ConfigArgs *ca )
+{
+ transformation_info *info = ca->ca_private;
+ datamorph_interval_bound *bound;
+ uint64_t unsigned_bound;
+ int64_t signed_bound;
+ char *ptr = ca->value_bv.bv_val + ca->value_bv.bv_len;
+ int flag;
+
+ if ( !info ) {
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ info = ov->wip_transformation;
+ assert( ca->op == SLAP_CONFIG_ADD );
+ }
+
+ switch ( ca->type ) {
+ case DATAMORPH_INT_LOWER:
+ bound = &info->ti_int.lower;
+ flag = DATAMORPH_FLAG_LOWER;
+ break;
+ case DATAMORPH_INT_UPPER:
+ bound = &info->ti_int.upper;
+ flag = DATAMORPH_FLAG_UPPER;
+ break;
+ default:
+ assert(0);
+ }
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ char buf[24];
+ struct berval bv = { .bv_val = buf };
+
+ if ( !(info->ti_int.flags & flag) ) {
+ /* Bound not set, do not emit */
+ return LDAP_SUCCESS;
+ }
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ bv.bv_len = sprintf( buf, "%" PRId64, bound->i );
+ } else {
+ bv.bv_len = sprintf( buf, "%" PRIu64, bound->u );
+ }
+ ber_dupbv_x( &ca->value_bv, &bv, ca->ca_op->o_tmpmemctx );
+
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ info->ti_int.flags &= ~flag;
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ bound->i = (flag == DATAMORPH_FLAG_LOWER) ? INT64_MIN : INT64_MAX;
+ } else {
+ bound->u = (flag == DATAMORPH_FLAG_LOWER) ? 0 : UINT64_MAX;
+ }
+ return LDAP_SUCCESS;
+ }
+
+ /* FIXME: if attributes in the Add operation come in the wrong order
+ * (signed=true after the bound definition), we can't check the interval
+ * sanity. */
+ /*
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_bound = strtoll( ca->value_bv.bv_val, &ptr, 10 );
+ } else {
+ unsigned_bound = strtoull( ca->value_bv.bv_val, &ptr, 10 );
+ }
+ */
+ /* Also, no idea what happens in the case of big-endian, hopefully,
+ * it behaves the same */
+ unsigned_bound = strtoull( ca->value_bv.bv_val, &ptr, 10 );
+ signed_bound = (int64_t)unsigned_bound;
+
+ if ( *ca->value_bv.bv_val == '\0' || *ptr != '\0' ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "failed to parse '%s' as integer",
+ ca->value_bv.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ ch_free( ca->value_bv.bv_val );
+
+ info->ti_int.flags |= flag;
+ switch ( info->ti_int.size ) {
+ case 1:
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ /* See FIXME above
+ if ( signed_bound < INT8_MIN || signed_bound > INT8_MAX ) {
+ goto fail;
+ }
+ */
+ } else {
+ /* See FIXME above
+ if ( unsigned_bound > UINT8_MAX ) {
+ goto fail;
+ }
+ */
+ }
+ break;
+ case 2:
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ /* See FIXME above
+ if ( signed_bound < INT16_MIN || signed_bound > INT16_MAX ) {
+ goto fail;
+ }
+ */
+ } else {
+ /* See FIXME above
+ if ( unsigned_bound > UINT16_MAX ) {
+ goto fail;
+ }
+ */
+ }
+ break;
+ case 4:
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ /* See FIXME above
+ if ( signed_bound < INT32_MIN || signed_bound > INT32_MAX ) {
+ goto fail;
+ }
+ */
+ } else {
+ /* See FIXME above
+ if ( unsigned_bound > UINT32_MAX ) {
+ goto fail;
+ }
+ */
+ }
+ break;
+ case 8:
+ break;
+ default:
+ /* Should only happen in these two cases:
+ * 1. datamorph_size not yet encountered for this one (when
+ * processing slapd.conf)
+ * 2. When someone runs a fun modification on the config entry
+ * messing with more attributes at once
+ *
+ * The error message is expected to be helpful only for the former,
+ * so use the slapd.conf name.
+ */
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "datamorph_size has to be set first!" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ bound->i = signed_bound;
+ } else {
+ bound->u = unsigned_bound;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_set_value( ConfigArgs *ca )
+{
+ datamorph_enum_mapping *mapping = ca->ca_private;
+ char *s = ca->value_bv.bv_val;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ /* We generate the value as part of the RDN, don't add anything */
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ ch_free( mapping->wire_value.bv_val );
+ BER_BVZERO( &mapping->wire_value );
+ /* TODO: remove from info->ti_enum.to_db? */
+ return LDAP_SUCCESS;
+ }
+
+ /* As long as this attribute can be in the RDN,
+ * we have to expect the '{n}' prefix */
+ if ( *s == '{' ) {
+ ber_len_t len;
+ s = memchr( s, '}', ca->value_bv.bv_len );
+ if ( !s ) {
+ ca->reply.err = LDAP_UNDEFINED_TYPE;
+ return ca->reply.err;
+ }
+ s += 1;
+
+ len = ca->value_bv.bv_len - ( s - ca->value_bv.bv_val );
+ ber_str2bv( s, len, 1, &mapping->wire_value );
+ ch_free( ca->value_bv.bv_val );
+ } else {
+ mapping->wire_value = ca->value_bv;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_set_index( ConfigArgs *ca )
+{
+ datamorph_enum_mapping *mapping = ca->ca_private;
+ struct berval *from_db = mapping->transformation->ti_enum.from_db;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_int = mapping->db_value;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ BER_BVZERO( &from_db[mapping->db_value] );
+ return LDAP_SUCCESS;
+ }
+
+ if ( ca->value_int < 0 || ca->value_int >= 256 ) {
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ } else if ( !BER_BVISNULL( &from_db[ca->value_int] ) ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg), "duplicate index %d",
+ ca->value_int );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ mapping->db_value = ca->value_int;
+ from_db[ca->value_int] = mapping->wire_value;
+
+ return LDAP_SUCCESS;
+}
+
+/* Called when processing slapd.conf only,
+ * cn=config uses the objectclass to decide which type we're dealing with.
+ */
+static int
+datamorph_add_transformation( ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ transformation_info *info;
+
+ if ( ov->wip_transformation ) {
+ /* We checked everything as were processing the lines */
+ int rc = ldap_avl_insert( &ov->transformations, ov->wip_transformation,
+ transformation_info_cmp, ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+ }
+
+ info = ch_calloc( 1, sizeof(transformation_info) );
+ ov->wip_transformation = ca->ca_private = info;
+
+ if ( !strcasecmp( ca->argv[1], "enum" ) ) {
+ info->type = DATAMORPH_ENUM;
+ } else if ( !strcasecmp( ca->argv[1], "int" ) ) {
+ info->type = DATAMORPH_INT;
+ } else {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "unknown transformation type '%s'", ca->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+
+ ca->value_string = strdup( ca->argv[2] );
+
+ return datamorph_set_attribute( ca );
+}
+
+static int
+datamorph_add_mapping( ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ transformation_info *info = ov->wip_transformation;
+ datamorph_enum_mapping *mapping;
+ int rc = LDAP_CONSTRAINT_VIOLATION;
+
+ if ( !info ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg), "no attribute configured" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ goto done;
+ }
+
+ mapping = ch_calloc( 1, sizeof(datamorph_enum_mapping) );
+ mapping->transformation = info;
+ ca->ca_private = mapping;
+
+ ber_str2bv( ca->argv[2], 0, 1, &ca->value_bv );
+ rc = datamorph_set_value( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ rc = lutil_atoix( &ca->value_int, ca->argv[1], 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg), "invalid integer %s",
+ ca->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ goto done;
+ }
+
+ rc = datamorph_set_index( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+done:
+ if ( rc == LDAP_SUCCESS ) {
+ rc = ldap_avl_insert( &info->ti_enum.to_db, mapping,
+ transformation_mapping_cmp, ldap_avl_dup_error );
+ }
+ if ( rc ) {
+ ca->reply.err = rc;
+ }
+
+ return rc;
+}
+
+static int
+datamorph_ldadd_info_cleanup( ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ transformation_info *info = ca->ca_private;
+
+ if ( ca->reply.err != LDAP_SUCCESS ) {
+ /* Not reached since cleanup is only called on success */
+fail:
+ ch_free( info );
+ return LDAP_SUCCESS;
+ }
+
+ if ( ldap_avl_insert( &ov->transformations, info, transformation_info_cmp,
+ ldap_avl_dup_error ) ) {
+ goto fail;
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_ldadd_transformation(
+ CfEntryInfo *cei,
+ Entry *e,
+ ConfigArgs *ca,
+ datamorph_type type )
+{
+ transformation_info *info;
+
+ if ( cei->ce_type != Cft_Overlay || !cei->ce_bi ||
+ cei->ce_bi->bi_cf_ocs != datamorph_ocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ info = ch_calloc( 1, sizeof(transformation_info) );
+ info->type = type;
+
+ ca->bi = cei->ce_bi;
+ ca->ca_private = info;
+ config_push_cleanup( ca, datamorph_ldadd_info_cleanup );
+ /* config_push_cleanup is only run in the case of online config but we use it to
+ * enable the new config when done with the entry */
+ ca->lineno = 0;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_ldadd_enum( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ return datamorph_ldadd_transformation( cei, e, ca, DATAMORPH_ENUM );
+}
+
+static int
+datamorph_ldadd_interval( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ return datamorph_ldadd_transformation( cei, e, ca, DATAMORPH_INT );
+}
+
+static int
+datamorph_ldadd_mapping_cleanup( ConfigArgs *ca )
+{
+ datamorph_enum_mapping *mapping = ca->ca_private;
+ transformation_info *info = mapping->transformation;
+
+ if ( ca->reply.err != LDAP_SUCCESS ) {
+ /* Not reached since cleanup is only called on success */
+fail:
+ datamorph_mapping_free( mapping );
+ return LDAP_SUCCESS;
+ }
+
+ if ( ldap_avl_insert( &info->ti_enum.to_db, mapping, transformation_mapping_cmp,
+ ldap_avl_dup_error ) ) {
+ goto fail;
+ }
+ info->ti_enum.from_db[mapping->db_value] = mapping->wire_value;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_ldadd_mapping( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ transformation_info *info;
+ datamorph_enum_mapping *mapping;
+ CfEntryInfo *parent = cei->ce_parent;
+
+ if ( cei->ce_type != Cft_Misc || !parent || !parent->ce_bi ||
+ parent->ce_bi->bi_cf_ocs != datamorph_ocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ info = cei->ce_private;
+
+ mapping = ch_calloc( 1, sizeof(datamorph_enum_mapping) );
+ mapping->transformation = info;
+
+ ca->ca_private = mapping;
+ config_push_cleanup( ca, datamorph_ldadd_mapping_cleanup );
+ /* config_push_cleanup is only run in the case of online config but we use it to
+ * enable the new config when done with the entry */
+ ca->lineno = 0;
+
+ return LDAP_SUCCESS;
+}
+
+struct datamorph_cfadd_args {
+ Operation *op;
+ SlapReply *rs;
+ Entry *p;
+ ConfigArgs *ca;
+ int index;
+};
+
+static int
+datamorph_config_build_enum( void *item, void *arg )
+{
+ datamorph_enum_mapping *mapping = item;
+ struct datamorph_cfadd_args *args = arg;
+ struct berval rdn;
+ Entry *e;
+ char *p;
+ ber_len_t index;
+
+ rdn.bv_len = snprintf( args->ca->cr_msg, sizeof(args->ca->cr_msg),
+ "olcDatamorphValue={%d}", args->index++ );
+ rdn.bv_val = args->ca->cr_msg;
+ p = rdn.bv_val + rdn.bv_len;
+
+ rdn.bv_len += mapping->wire_value.bv_len;
+ for ( index = 0; index < mapping->wire_value.bv_len; index++ ) {
+ if ( RDN_NEEDSESCAPE(mapping->wire_value.bv_val[index]) ) {
+ rdn.bv_len++;
+ *p++ = '\\';
+ }
+ *p++ = mapping->wire_value.bv_val[index];
+ }
+ *p = '\0';
+
+ args->ca->ca_private = mapping;
+ args->ca->ca_op = args->op;
+ e = config_build_entry( args->op, args->rs, args->p->e_private, args->ca,
+ &rdn, &datamorph_ocs[4], NULL );
+ assert( e );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_config_build_attr( void *item, void *arg )
+{
+ transformation_info *info = item;
+ struct datamorph_cfadd_args *args = arg;
+ struct berval rdn;
+ ConfigOCs *oc;
+ Entry *e;
+
+ rdn.bv_len = snprintf( args->ca->cr_msg, sizeof(args->ca->cr_msg),
+ "olcDatamorphAttribute={%d}%s", args->index++,
+ info->attr->ad_cname.bv_val );
+ rdn.bv_val = args->ca->cr_msg;
+
+ switch ( info->type ) {
+ case DATAMORPH_ENUM:
+ oc = &datamorph_ocs[2];
+ break;
+ case DATAMORPH_INT:
+ oc = &datamorph_ocs[3];
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ args->ca->ca_private = info;
+ args->ca->ca_op = args->op;
+ e = config_build_entry(
+ args->op, args->rs, args->p->e_private, args->ca, &rdn, oc, NULL );
+ assert( e );
+
+ if ( info->type == DATAMORPH_ENUM ) {
+ struct datamorph_cfadd_args new_args = *args;
+ new_args.p = e;
+ new_args.index = 0;
+
+ return ldap_avl_apply( info->ti_enum.to_db, datamorph_config_build_enum,
+ &new_args, 1, AVL_PREORDER );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ struct datamorph_cfadd_args args = {
+ .op = op,
+ .rs = rs,
+ .p = p,
+ .ca = ca,
+ .index = 0,
+ };
+
+ if ( ov->wip_transformation ) {
+ /* There is one last item that is unfinished */
+ int rc = ldap_avl_insert( &ov->transformations, ov->wip_transformation,
+ transformation_info_cmp, ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+ }
+
+ return ldap_avl_apply( ov->transformations, &datamorph_config_build_attr, &args,
+ 1, AVL_PREORDER );
+}
+
+static slap_overinst datamorph;
+
+static int
+datamorph_db_init( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ datamorph_info *ov;
+
+ /* TODO: can this be global? */
+ if ( SLAP_ISGLOBALOVERLAY(be) ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph overlay must be instantiated "
+ "within a database.\n" );
+ return 1;
+ }
+
+ ov = ch_calloc( 1, sizeof(datamorph_info) );
+ on->on_bi.bi_private = ov;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+
+ if ( ov ) {
+ ldap_avl_free( ov->transformations, datamorph_info_free );
+ }
+ ch_free( ov );
+
+ return LDAP_SUCCESS;
+}
+
+int
+datamorph_initialize()
+{
+ int rc, i;
+
+ datamorph.on_bi.bi_type = "datamorph";
+ datamorph.on_bi.bi_db_init = datamorph_db_init;
+ datamorph.on_bi.bi_db_destroy = datamorph_db_destroy;
+
+ datamorph.on_bi.bi_op_add = datamorph_op_add;
+ datamorph.on_bi.bi_op_compare = datamorph_op_compare;
+ datamorph.on_bi.bi_op_modify = datamorph_op_mod;
+ datamorph.on_bi.bi_op_modrdn = datamorph_op_modrdn;
+ datamorph.on_bi.bi_op_search = datamorph_op_search;
+ datamorph.on_response = datamorph_response;
+
+ datamorph.on_bi.bi_entry_release_rw = datamorph_entry_release_rw;
+ datamorph.on_bi.bi_entry_get_rw = datamorph_entry_get_rw;
+
+ datamorph.on_bi.bi_cf_ocs = datamorph_ocs;
+
+ for ( i = 0; datamorph_syntax_defs[i].sd_desc != NULL; i++ ) {
+ rc = register_syntax( &datamorph_syntax_defs[i] );
+
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph_initialize: "
+ "error registering syntax %s\n",
+ datamorph_syntax_defs[i].sd_desc );
+ return rc;
+ }
+ }
+
+ datamorph_base_syntax = syn_find( DATAMORPH_SYNTAX_BASE );
+ assert( datamorph_base_syntax );
+
+ for ( i = 0; datamorph_mrule_defs[i].mrd_desc != NULL; i++ ) {
+ rc = register_matching_rule( &datamorph_mrule_defs[i] );
+
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph_initialize: "
+ "error registering matching rule %s\n",
+ datamorph_mrule_defs[i].mrd_desc );
+ return rc;
+ }
+ }
+
+ rc = config_register_schema( datamorph_cfg, datamorph_ocs );
+ if ( rc ) return rc;
+
+ return overlay_register( &datamorph );
+}
+
+#if SLAPD_OVER_DATAMORPH == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return datamorph_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_DATAMORPH */