Преглед изворни кода

Ticket #279 - filter normalization does not use matching rules

https://fedorahosted.org/389/ticket/279
Resolves: Ticket #279
Bug Description: filter normalization does not use matching rules
Reviewed by: nhosoi (Thanks!)
Branch: master
Fix Description: When normalizing the values in a filter, the normalization
must take into account the type of filter being used, and the matching
rules associated with the attribute, corresponding to the type of filter.
Added a mr_normalize function for matching rules.  This is primarily needed
by the cis and ces associated matching rules, for the case where the
normalization function provided by the syntax is different than the one
associated with the matching rule.  For example, Directory String (cis)
by default uses a case insensitive normalization function, but if you are
using a caseExact matching rule you must use a case sensitive
normalization function.  Added the new function
slapi_attr_value_normalize_ext() to allow passing in the type of search
filter to normalize for.
Platforms tested: RHEL6 x86_64
Flag Day: yes - plugin structure size change
Doc impact: yes - document the new function
Rich Megginson пре 13 година
родитељ
комит
7b03c971dd

+ 5 - 5
ldap/servers/plugins/syntaxes/ces.c

@@ -121,7 +121,7 @@ IA5STRING_SYNTAX_OID, 0, caseExactIA5Match_syntaxes}, /* matching rule desc */
  {"caseExactIA5Match-mr", VENDOR, DS_PACKAGE_VERSION, "caseExactIA5Match matching rule plugin"}, /* plugin desc */
    caseExactIA5Match_names, /* matching rule name/oid/aliases */
    NULL, NULL, ces_filter_ava, NULL, ces_values2keys,
-   ces_assertion2keys_ava, NULL, ces_compare},
+   ces_assertion2keys_ava, NULL, ces_compare, ces_normalize},
 {{"2.5.13.5", NULL, "caseExactMatch", "The caseExactMatch rule compares an assertion value of the Directory "
 "String syntax to an attribute value of a syntax (e.g., the Directory "
 "String, Printable String, Country String, or Telephone Number syntax) "
@@ -141,7 +141,7 @@ DIRSTRING_SYNTAX_OID, 0, dirStringCompat_syntaxes}, /* matching rule desc */
  {"caseExactMatch-mr", VENDOR, DS_PACKAGE_VERSION, "caseExactMatch matching rule plugin"}, /* plugin desc */
    caseExactMatch_names, /* matching rule name/oid/aliases */
    NULL, NULL, ces_filter_ava, NULL, ces_values2keys,
-   ces_assertion2keys_ava, NULL, ces_compare},
+   ces_assertion2keys_ava, NULL, ces_compare, ces_normalize},
 {{"2.5.13.6", NULL, "caseExactOrderingMatch", "The caseExactOrderingMatch rule compares an assertion value of the "
 "Directory String syntax to an attribute value of a syntax (e.g., the "
 "Directory String, Printable String, Country String, or Telephone "
@@ -159,7 +159,7 @@ DIRSTRING_SYNTAX_OID, 0, dirStringCompat_syntaxes}, /* matching rule desc */
  {"caseExactOrderingMatch-mr", VENDOR, DS_PACKAGE_VERSION, "caseExactOrderingMatch matching rule plugin"}, /* plugin desc */
    caseExactOrderingMatch_names, /* matching rule name/oid/aliases */
    NULL, NULL, ces_filter_ava, NULL, ces_values2keys,
-   ces_assertion2keys_ava, NULL, ces_compare},
+   ces_assertion2keys_ava, NULL, ces_compare, ces_normalize},
 {{"2.5.13.7", NULL, "caseExactSubstringsMatch", "The caseExactSubstringsMatch rule compares an assertion value of the "
 "Substring Assertion syntax to an attribute value of a syntax (e.g., "
 "the Directory String, Printable String, Country String, or Telephone "
@@ -182,7 +182,7 @@ DIRSTRING_SYNTAX_OID, 0, dirStringCompat_syntaxes}, /* matching rule desc */
  {"caseExactSubstringsMatch-mr", VENDOR, DS_PACKAGE_VERSION, "caseExactSubstringsMatch matching rule plugin"}, /* plugin desc */
  caseExactSubstringsMatch_names, /* matching rule name/oid/aliases */
  NULL, NULL, NULL, ces_filter_sub, ces_values2keys,
- NULL, ces_assertion2keys_sub, ces_compare},
+ NULL, ces_assertion2keys_sub, ces_compare, ces_normalize},
 {{CASEEXACTIA5SUBSTRINGSMATCH_OID, NULL, "caseExactIA5SubstringsMatch", "The caseExactIA5SubstringsMatch rule compares an assertion value of the "
 "Substring Assertion syntax to an attribute value of a syntax (e.g., "
 "the IA5 syntax) whose corresponding ASN.1 type is IA5 String or "
@@ -204,7 +204,7 @@ DIRSTRING_SYNTAX_OID, 0, dirStringCompat_syntaxes}, /* matching rule desc */
  {"caseExactIA5SubstringsMatch-mr", VENDOR, DS_PACKAGE_VERSION, "caseExactIA5SubstringsMatch matching rule plugin"}, /* plugin desc */
    caseExactIA5SubstringsMatch_names, /* matching rule name/oid/aliases */
    NULL, NULL, NULL, ces_filter_sub, ces_values2keys,
-   NULL, ces_assertion2keys_sub, ces_compare}
+   NULL, ces_assertion2keys_sub, ces_compare, ces_normalize}
 };
 
 static size_t mr_plugin_table_size = sizeof(mr_plugin_table)/sizeof(mr_plugin_table[0]);

+ 13 - 13
ldap/servers/plugins/syntaxes/cis.c

@@ -215,14 +215,14 @@ static struct mr_plugin_def mr_plugin_table[] = {
  {"generalizedTimeMatch-mr", VENDOR, DS_PACKAGE_VERSION, "generalizedTimeMatch matching rule plugin"}, /* plugin desc */
  generalizedTimeMatch_names, /* matching rule name/oid/aliases */
  NULL, NULL, cis_filter_ava, NULL, cis_values2keys,
- cis_assertion2keys_ava, NULL, cis_compare},
+ cis_assertion2keys_ava, NULL, cis_compare, cis_normalize},
 {{GENERALIZEDTIMEORDERINGMATCH_OID, NULL /* no alias? */,
   "generalizedTimeOrderingMatch", "The rule evaluates to TRUE if and only if the attribute value represents a universal coordinated time that is earlier than the universal coordinated time represented by the assertion value.",
   GENERALIZEDTIME_SYNTAX_OID, 0 /* not obsolete */, NULL /* no other syntaxes supported */ },
  {"generalizedTimeOrderingMatch-mr", VENDOR, DS_PACKAGE_VERSION, "generalizedTimeOrderingMatch matching rule plugin"}, /* plugin desc */
  generalizedTimeOrderingMatch_names, /* matching rule name/oid/aliases */
  NULL, NULL, cis_filter_ava, NULL, cis_values2keys,
- cis_assertion2keys_ava, NULL, cis_compare},
+ cis_assertion2keys_ava, NULL, cis_compare, cis_normalize},
 /* strictly speaking, boolean is case sensitive */
 {{"2.5.13.13", NULL, "booleanMatch", "The booleanMatch rule compares an assertion value of the Boolean "
 "syntax to an attribute value of a syntax (e.g., the Boolean syntax) "
@@ -232,7 +232,7 @@ static struct mr_plugin_def mr_plugin_table[] = {
  {"booleanMatch-mr", VENDOR, DS_PACKAGE_VERSION, "booleanMatch matching rule plugin"}, /* plugin desc */
    booleanMatch_names, /* matching rule name/oid/aliases */
    NULL, NULL, cis_filter_ava, NULL, cis_values2keys,
-   cis_assertion2keys_ava, NULL, cis_compare},
+   cis_assertion2keys_ava, NULL, cis_compare, cis_normalize},
 {{"1.3.6.1.4.1.1466.109.114.2", NULL, "caseIgnoreIA5Match", "The caseIgnoreIA5Match rule compares an assertion value of the IA5 "
 "String syntax to an attribute value of a syntax (e.g., the IA5 String "
 "syntax) whose corresponding ASN.1 type is IA5String.  "
@@ -247,7 +247,7 @@ static struct mr_plugin_def mr_plugin_table[] = {
  {"caseIgnoreIA5Match-mr", VENDOR, DS_PACKAGE_VERSION, "caseIgnoreIA5Match matching rule plugin"}, /* plugin desc */
    caseIgnoreIA5Match_names, /* matching rule name/oid/aliases */
    NULL, NULL, cis_filter_ava, NULL, cis_values2keys,
-   cis_assertion2keys_ava, NULL, cis_compare},
+   cis_assertion2keys_ava, NULL, cis_compare, cis_normalize},
 {{"1.3.6.1.4.1.1466.109.114.3", NULL, "caseIgnoreIA5SubstringsMatch", "The caseIgnoreIA5SubstringsMatch rule compares an assertion value of "
 "the Substring Assertion syntax to an attribute value of a syntax "
 "(e.g., the IA5 String syntax) whose corresponding ASN.1 type is "
@@ -268,7 +268,7 @@ static struct mr_plugin_def mr_plugin_table[] = {
  {"caseIgnoreIA5SubstringsMatch-mr", VENDOR, DS_PACKAGE_VERSION, "caseIgnoreIA5SubstringsMatch matching rule plugin"}, /* plugin desc */
    caseIgnoreIA5SubstringsMatch_names, /* matching rule name/oid/aliases */
    NULL, NULL, NULL, cis_filter_sub, cis_values2keys,
-   NULL, cis_assertion2keys_sub, NULL},
+   NULL, cis_assertion2keys_sub, NULL, cis_normalize},
 {{"2.5.13.2", NULL, "caseIgnoreMatch", "The caseIgnoreMatch rule compares an assertion value of the Directory "
 "String syntax to an attribute value of a syntax (e.g., the Directory "
 "String, Printable String, Country String, or Telephone Number syntax) "
@@ -285,7 +285,7 @@ static struct mr_plugin_def mr_plugin_table[] = {
  {"caseIgnoreMatch-mr", VENDOR, DS_PACKAGE_VERSION, "caseIgnoreMatch matching rule plugin"}, /* plugin desc */
    caseIgnoreMatch_names, /* matching rule name/oid/aliases */
    NULL, NULL, cis_filter_ava, NULL, cis_values2keys,
-   cis_assertion2keys_ava, NULL, cis_compare},
+   cis_assertion2keys_ava, NULL, cis_compare, cis_normalize},
 {{"2.5.13.3", NULL, "caseIgnoreOrderingMatch", "The caseIgnoreOrderingMatch rule compares an assertion value of the "
 "Directory String syntax to an attribute value of a syntax (e.g., the "
 "Directory String, Printable String, Country String, or Telephone "
@@ -302,7 +302,7 @@ static struct mr_plugin_def mr_plugin_table[] = {
  {"caseIgnoreOrderingMatch-mr", VENDOR, DS_PACKAGE_VERSION, "caseIgnoreOrderingMatch matching rule plugin"}, /* plugin desc */
    caseIgnoreOrderingMatch_names, /* matching rule name/oid/aliases */
    NULL, NULL, cis_filter_ava, NULL, cis_values2keys,
-   cis_assertion2keys_ava, NULL, cis_compare},
+   cis_assertion2keys_ava, NULL, cis_compare, cis_normalize},
 {{"2.5.13.4", NULL, "caseIgnoreSubstringsMatch", "The caseIgnoreSubstringsMatch rule compares an assertion value of the "
 "Substring Assertion syntax to an attribute value of a syntax (e.g., "
 "the Directory String, Printable String, Country String, or Telephone "
@@ -324,7 +324,7 @@ static struct mr_plugin_def mr_plugin_table[] = {
  {"caseIgnoreSubstringsMatch-mr", VENDOR, DS_PACKAGE_VERSION, "caseIgnoreSubstringsMatch matching rule plugin"}, /* plugin desc */
    caseIgnoreSubstringsMatch_names, /* matching rule name/oid/aliases */
    NULL, NULL, NULL, cis_filter_sub, cis_values2keys,
-   NULL, cis_assertion2keys_sub, cis_compare},
+   NULL, cis_assertion2keys_sub, cis_compare, cis_normalize},
 {{"2.5.13.11", NULL, "caseIgnoreListMatch", "The caseIgnoreListMatch rule compares an assertion value that is a "
 "sequence of strings to an attribute value of a syntax (e.g., the "
 "Postal Address syntax) whose corresponding ASN.1 type is a SEQUENCE "
@@ -344,7 +344,7 @@ static struct mr_plugin_def mr_plugin_table[] = {
  {"caseIgnoreListMatch-mr", VENDOR, DS_PACKAGE_VERSION, "caseIgnoreListMatch matching rule plugin"}, /* plugin desc */
    caseIgnoreListMatch_names, /* matching rule name/oid/aliases */
    NULL, NULL, cis_filter_ava, NULL, cis_values2keys,
-   cis_assertion2keys_ava, NULL, cis_compare},
+   cis_assertion2keys_ava, NULL, cis_compare, cis_normalize},
 {{"2.5.13.12", NULL, "caseIgnoreListSubstringsMatch", "The caseIgnoreListSubstringsMatch rule compares an assertion value of "
 "the Substring Assertion syntax to an attribute value of a syntax "
 "(e.g., the Postal Address syntax) whose corresponding ASN.1 type is a "
@@ -363,7 +363,7 @@ static struct mr_plugin_def mr_plugin_table[] = {
  {"caseIgnoreListSubstringsMatch-mr", VENDOR, DS_PACKAGE_VERSION, "caseIgnoreListSubstringsMatch matching rule plugin"}, /* plugin desc */
    caseIgnoreListSubstringsMatch_names, /* matching rule name/oid/aliases */
    NULL, NULL, NULL, cis_filter_sub, cis_values2keys,
-   NULL, cis_assertion2keys_sub, cis_compare},
+   NULL, cis_assertion2keys_sub, cis_compare, cis_normalize},
 {{"2.5.13.0", NULL, "objectIdentifierMatch", "The objectIdentifierMatch rule compares an assertion value of the OID "
 "syntax to an attribute value of a syntax (e.g., the OID syntax) whose "
 "corresponding ASN.1 type is OBJECT IDENTIFIER. "
@@ -379,7 +379,7 @@ OID_SYNTAX_OID, 0, NULL /* OID syntax only for now */}, /* matching rule desc */
  {"objectIdentifierMatch-mr", VENDOR, DS_PACKAGE_VERSION, "objectIdentifierMatch matching rule plugin"}, /* plugin desc */
  objectIdentifierMatch_names, /* matching rule name/oid/aliases */
  NULL, NULL, cis_filter_ava, NULL, cis_values2keys,
- cis_assertion2keys_ava, NULL, cis_compare},
+ cis_assertion2keys_ava, NULL, cis_compare, cis_normalize},
 {{"2.5.13.31", NULL, "directoryStringFirstComponentMatch", "The directoryStringFirstComponentMatch rule compares an assertion "
 "value of the Directory String syntax to an attribute value of a "
 "syntax whose corresponding ASN.1 type is a SEQUENCE with a mandatory "
@@ -393,7 +393,7 @@ OID_SYNTAX_OID, 0, NULL /* OID syntax only for now */}, /* matching rule desc */
  {"directoryStringFirstComponentMatch-mr", VENDOR, DS_PACKAGE_VERSION, "directoryStringFirstComponentMatch matching rule plugin"}, /* plugin desc */
    directoryStringFirstComponentMatch_names, /* matching rule name/oid/aliases */
    NULL, NULL, cis_filter_ava, NULL, cis_values2keys,
-   cis_assertion2keys_ava, NULL, NULL},
+   cis_assertion2keys_ava, NULL, NULL, cis_normalize},
 {{"2.5.13.30", NULL, "objectIdentifierFirstComponentMatch",
 "The objectIdentifierFirstComponentMatch rule compares an assertion "
 "value of the OID syntax to an attribute value of a syntax (e.g., the "
@@ -411,7 +411,7 @@ OID_SYNTAX_OID, 0, NULL /* OID syntax only for now */}, /* matching rule desc */
  {"objectIdentifierFirstComponentMatch-mr", VENDOR, DS_PACKAGE_VERSION, "objectIdentifierFirstComponentMatch matching rule plugin"}, /* plugin desc */
    objectIdentifierFirstComponentMatch_names, /* matching rule name/oid/aliases */
    NULL, NULL, cis_filter_ava, NULL, cis_values2keys,
-   cis_assertion2keys_ava, NULL, NULL}
+   cis_assertion2keys_ava, NULL, NULL, cis_normalize}
 };
 
 static size_t mr_plugin_table_size = sizeof(mr_plugin_table)/sizeof(mr_plugin_table[0]);

+ 1 - 0
ldap/servers/plugins/syntaxes/syntax.h

@@ -143,6 +143,7 @@ struct mr_plugin_def {
     IFP	mr_assertion2keys_ava; /* SLAPI_PLUGIN_MR_ASSERTION2KEYS_AVA */
     IFP	mr_assertion2keys_sub; /* SLAPI_PLUGIN_MR_ASSERTION2KEYS_SUB */
     IFP	mr_compare; /* SLAPI_PLUGIN_MR_COMPARE - only for ORDERING */
+    VFPV	mr_normalize;
 };
 
 int syntax_register_matching_rule_plugins(struct mr_plugin_def mr_plugin_table[], size_t mr_plugin_table_size, IFP matching_rule_plugin_init);

+ 1 - 0
ldap/servers/plugins/syntaxes/syntax_common.c

@@ -100,6 +100,7 @@ syntax_matching_rule_plugin_init(
 			rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_MR_ASSERTION2KEYS_SUB, mrpd->mr_assertion2keys_sub);
 			rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_MR_NAMES, mrpd->mr_names);
 			rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_MR_COMPARE, mrpd->mr_compare);
+			rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_MR_NORMALIZE, mrpd->mr_normalize);
 			break;
 		}
 	}

+ 5 - 4
ldap/servers/slapd/filter.c

@@ -1056,7 +1056,8 @@ filter_normalize_ava( struct slapi_filter *f, PRBool norm_values )
         char *newval = NULL;
         /* NOTE: assumes ava->ava_value.bv_val is NULL terminated - get_ava/ber_scanf 'o'
            will NULL terminate the string by default */
-        slapi_attr_value_normalize(NULL, NULL, ava->ava_type, ava->ava_value.bv_val, 1, &newval);
+        slapi_attr_value_normalize_ext(NULL, NULL, ava->ava_type,
+                                       ava->ava_value.bv_val, 1, &newval, f->f_choice);
         if (newval && (newval != ava->ava_value.bv_val)) {
             slapi_ch_free_string(&ava->ava_value.bv_val);
             ava->ava_value.bv_val = newval;
@@ -1086,7 +1087,7 @@ filter_normalize_subfilt( struct slapi_filter *f, PRBool norm_values )
 		int ii;
 
 		slapi_attr_init(&attr, sf->sf_type);
-		slapi_attr_value_normalize(NULL, &attr, NULL, sf->sf_initial, 1, &newval);
+		slapi_attr_value_normalize_ext(NULL, &attr, NULL, sf->sf_initial, 1, &newval, f->f_choice);
 		if (newval && (newval != sf->sf_initial)) {
 			slapi_ch_free_string(&sf->sf_initial);
 			sf->sf_initial = newval;
@@ -1094,7 +1095,7 @@ filter_normalize_subfilt( struct slapi_filter *f, PRBool norm_values )
 		for (ii = 0; sf->sf_any && sf->sf_any[ii]; ++ii) {
 			newval = NULL;
 			/* do not trim spaces of sf_any values - see string_filter_sub() */
-			slapi_attr_value_normalize(NULL, &attr, NULL, sf->sf_any[ii], 0, &newval);
+			slapi_attr_value_normalize_ext(NULL, &attr, NULL, sf->sf_any[ii], 0, &newval, f->f_choice);
 			if (newval && (newval != sf->sf_any[ii])) {
 				slapi_ch_free_string(&sf->sf_any[ii]);
 				sf->sf_any[ii] = newval;
@@ -1102,7 +1103,7 @@ filter_normalize_subfilt( struct slapi_filter *f, PRBool norm_values )
 		}
 		newval = NULL;
 		/* do not trim spaces of sf_final values - see string_filter_sub() */
-		slapi_attr_value_normalize(NULL, &attr, NULL, sf->sf_final, 0, &newval);
+		slapi_attr_value_normalize_ext(NULL, &attr, NULL, sf->sf_final, 0, &newval, f->f_choice);
 		if (newval && (newval != sf->sf_final)) {
 			slapi_ch_free_string(&sf->sf_final);
 			sf->sf_final = newval;

+ 12 - 0
ldap/servers/slapd/pblock.c

@@ -1595,6 +1595,12 @@ slapi_pblock_get( Slapi_PBlock *pblock, int arg, void *value )
 		}
 		(*(IFP *)value) = pblock->pb_plugin->plg_mr_compare;
 		break;
+	case SLAPI_PLUGIN_MR_NORMALIZE:
+		if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_MATCHINGRULE ) {
+			return( -1 );
+		}
+		(*(VFPV *)value) = pblock->pb_plugin->plg_mr_normalize;
+		break;
 
 	/* seq arguments */
 	case SLAPI_SEQ_TYPE:
@@ -3157,6 +3163,12 @@ slapi_pblock_set( Slapi_PBlock *pblock, int arg, void *value )
 		}
 		pblock->pb_plugin->plg_mr_compare = (IFP) value;
 		break;
+	case SLAPI_PLUGIN_MR_NORMALIZE:
+		if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_MATCHINGRULE ) {
+			return( -1 );
+		}
+		pblock->pb_plugin->plg_mr_normalize = (VFPV) value;
+		break;
 
 	/* seq arguments */
 	case SLAPI_SEQ_TYPE:

+ 43 - 3
ldap/servers/slapd/plugin_syntax.c

@@ -929,13 +929,14 @@ slapi_attr_assertion2keys_sub( /* JCM SLOW FUNCTION */
 }
 
 void
-slapi_attr_value_normalize(
+slapi_attr_value_normalize_ext(
 	Slapi_PBlock *pb,
 	const Slapi_Attr *sattr, /* if sattr is NULL, type must be attr type name */
 	const char *type,
 	char *val,
 	int trim_spaces,
-	char **retval
+	char **retval,
+	unsigned long filter_type
 )
 {
 	Slapi_Attr myattr;
@@ -944,7 +945,33 @@ slapi_attr_value_normalize(
 	if (!sattr) {
 		sattr = slapi_attr_init(&myattr, type);
 	}
-	norm_fn = sattr->a_plugin->plg_syntax_normalize;
+
+	/* use the filter type to determine which matching rule to use */
+	switch (filter_type) {
+	case LDAP_FILTER_GE:
+	case LDAP_FILTER_LE:
+		if (sattr->a_mr_ord_plugin) {
+			norm_fn = sattr->a_mr_ord_plugin->plg_mr_normalize;
+		}
+		break;
+	case LDAP_FILTER_EQUALITY:
+		if (sattr->a_mr_eq_plugin) {
+			norm_fn = sattr->a_mr_eq_plugin->plg_mr_normalize;
+		}
+		break;
+	case LDAP_FILTER_SUBSTRINGS:
+		if (sattr->a_mr_sub_plugin) {
+			norm_fn = sattr->a_mr_sub_plugin->plg_mr_normalize;
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (!norm_fn) {
+		/* no matching rule specific normalizer specified - use syntax default */
+		norm_fn = sattr->a_plugin->plg_syntax_normalize;
+	}
 	if (norm_fn) {
 		(*norm_fn)(pb, val, trim_spaces, retval);
 	}
@@ -953,3 +980,16 @@ slapi_attr_value_normalize(
 	}
 	return;
 }
+
+void
+slapi_attr_value_normalize(
+	Slapi_PBlock *pb,
+	const Slapi_Attr *sattr, /* if sattr is NULL, type must be attr type name */
+	const char *type,
+	char *val,
+	int trim_spaces,
+	char **retval
+)
+{
+	return slapi_attr_value_normalize_ext(pb, sattr, type, val, trim_spaces, retval, 0);
+}

+ 2 - 0
ldap/servers/slapd/slap.h

@@ -1036,6 +1036,7 @@ struct slapdplugin {
 			int	plg_un_mr_flags;
 			char	**plg_un_mr_names;
 			IFP	plg_un_mr_compare; /* only for ORDERING */
+			VFPV	plg_un_mr_normalize;
 		} plg_un_mr;
 #define plg_mr_filter_create	plg_un.plg_un_mr.plg_un_mr_filter_create
 #define plg_mr_indexer_create	plg_un.plg_un_mr.plg_un_mr_indexer_create
@@ -1047,6 +1048,7 @@ struct slapdplugin {
 #define plg_mr_flags		plg_un.plg_un_mr.plg_un_mr_flags
 #define plg_mr_names		plg_un.plg_un_mr.plg_un_mr_names
 #define plg_mr_compare		plg_un.plg_un_mr.plg_un_mr_compare
+#define plg_mr_normalize	plg_un.plg_un_mr.plg_un_mr_normalize
 
 		/* syntax plugin structure */
 		struct plg_un_syntax_struct {

+ 35 - 0
ldap/servers/slapd/slapi-plugin.h

@@ -5306,6 +5306,40 @@ void slapi_attr_value_normalize(
 	char **retval
 );
 
+/**
+ * Normalize the given value using the matching rule associated with the
+ * given attribute and given filter type.  It will attempt to normalize
+ * the value in place. If it cannot, it will return the normalized value
+ * in retval.  If trim_spaces is true, whitepace characters will be trimmed
+ * from the ends of the string.  If sattr is NULL, the type will be used to look
+ * up the attribute syntax.  If sattr is not NULL, type is ignored.  If
+ * retval is set, the caller is responsible for freeing it.  The filter_type
+ * corresponds to the matching rule to use - LDAP_FILTER_GE or LDAP_FILTER_LE
+ * will use the ORDERING matching rule normalization function - LDAP_FILTER_EQUALITY
+ * will use the EQUALITY matching rule normalization function - LDAP_FILTER_SUBSTRINGS
+ * will use the SUBSTRINGS matching rule normalization function.  If the given
+ * filter_type is 0, or some other value other than specified above, or there is no
+ * matching rule corresponding to the given filter type, the default normalization
+ * function provided by the attribute syntax will be used.
+ *
+ * \param pb Slapi_PBlock to use
+ * \param sattr attribute to get the syntax from 
+ * \param type attribute to get the syntax from if sattr is NULL
+ * \param val value to normalize in place - must be NULL terminated
+ * \param trim_spaces trim whitespace from ends of string
+ * \param retval if value could not be normalized in place, this is the malloc'd memory containg the new value - caller must free
+ * \param filter_type one of the values specified above, or 0
+ */
+void slapi_attr_value_normalize_ext(
+	Slapi_PBlock *pb,
+	const Slapi_Attr *sattr, /* if sattr is NULL, type must be attr type name */
+	const char *type,
+	char *val,
+	int trim_spaces,
+	char **retval,
+	unsigned long filter_type
+);
+
 /*
  * internal operation and plugin callback routines
  */
@@ -6287,6 +6321,7 @@ typedef struct slapi_plugindesc {
 #define SLAPI_PLUGIN_MR_FLAGS		623
 #define SLAPI_PLUGIN_MR_NAMES		624
 #define SLAPI_PLUGIN_MR_COMPARE		625
+#define SLAPI_PLUGIN_MR_NORMALIZE	626
 
 /* Defined values of SLAPI_PLUGIN_MR_QUERY_OPERATOR: */
 #define SLAPI_OP_LESS					1