summaryrefslogtreecommitdiffstats
path: root/ldap/servers/plugins/collation
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/plugins/collation')
-rw-r--r--ldap/servers/plugins/collation/Makefile99
-rw-r--r--ldap/servers/plugins/collation/collate.c454
-rw-r--r--ldap/servers/plugins/collation/collate.h36
-rw-r--r--ldap/servers/plugins/collation/collation.def10
-rw-r--r--ldap/servers/plugins/collation/config.c178
-rw-r--r--ldap/servers/plugins/collation/config.h12
-rw-r--r--ldap/servers/plugins/collation/debug.c14
-rw-r--r--ldap/servers/plugins/collation/dllmain.c129
-rw-r--r--ldap/servers/plugins/collation/orfilter.c984
-rw-r--r--ldap/servers/plugins/collation/orfilter.h10
10 files changed, 1926 insertions, 0 deletions
diff --git a/ldap/servers/plugins/collation/Makefile b/ldap/servers/plugins/collation/Makefile
new file mode 100644
index 00000000..14619af7
--- /dev/null
+++ b/ldap/servers/plugins/collation/Makefile
@@ -0,0 +1,99 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+LDAP_SRC= ../../..
+MCOM_ROOT= ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST= $(OBJDIR)/lib/liblcoll
+LIBDIR= $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+INCLUDES+= -I../../slapd -I../../../include
+CFLAGS+=$(SLCFLAGS) -DSLAPD_LOGGING
+
+COLLATION_OBJS= collate.o config.o orfilter.o
+
+ifeq ($(ARCH), WINNT)
+COLLATION_OBJS+= debug.o
+COLLATION_DLL_OBJ=$(addprefix $(OBJDEST)/, dllmain.o)
+DEF_FILE:=./collation.def
+EXTRA_LIBS+= $(NSPRLINK) $(LDAP_SDK_LIBLDAP_DLL) $(LIBSLAPD)
+EXTRA_LIBS_DEP+= $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP+=$(LDAPSDK_DEP)
+endif
+
+# INCLUDES+= -I. -I$(ACLINC) -I$(MCOM_ROOT)/ldapserver/lib
+
+# ICU stuff
+INCLUDES+= $(ICU_INCLUDE)
+EXTRA_LIBS+=$(ICULINK)
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS+= $(LIBSLAPDLINK) $(NSPRLINK) $(LDAPLINK)
+EXTRA_LIBS_DEP+= $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP+=$(LDAPSDK_DEP)
+LD=ld
+endif
+
+OBJS= $(addprefix $(OBJDEST)/, $(COLLATION_OBJS))
+COLLATION= $(addprefix $(LIBDIR)/, $(COLLATION_DLL).$(DLL_SUFFIX))
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(COLLATION)
+ifeq (0, 1)
+# Where the heck did the compiler options come from?
+ @echo ARCH=$(ARCH)
+ @echo DEBUG=$(DEBUG)
+ @echo BUILD_OPT=$(BUILD_OPT)
+ @echo CFLAGS=$(CFLAGS)
+ @echo " MCC_DEBUG="$(MCC_DEBUG)
+ @echo " PLATFORMCFLAGS="$(PLATFORMCFLAGS)
+ @echo " ACFLAGS="$(ACFLAGS)
+ @echo " EXTRACFLAGS="$(EXTRACFLAGS)
+ @echo " UNPROTOCFLAGS="$(UNPROTOCFLAGS)
+ @echo " SLCFLAGS="$(SLCFLAGS)
+ @echo "ALDFLAGS="$(ALDFLAGS)
+ @echo "DLL_LDFLAGS="$(DLL_LDFLAGS)
+ @echo "DLL_EXPORT_FLAGS="$(DLL_EXPORT_FLAGS)
+endif
+
+ifeq ($(ARCH), WINNT)
+$(COLLATION): $(OBJS) $(COLLATION_DLL_OBJ) $(EXTRA_LIBS_DEP) $(DEF_FILE)
+ $(LINK_DLL) $(COLLATION_DLL_OBJ) $(EXTRA_LIBS) /DEF:$(DEF_FILE)
+else
+ifeq ($(ARCH), AIX)
+$(COLLATION): $(OBJS) $(COLLATION_DLL_OBJ) $(EXTRA_LIBS_DEP)
+ $(LINK_DLL) $(COLLATION_DLL_OBJ) $(EXTRA_LIBS)
+else
+$(COLLATION): $(OBJS) $(EXTRA_LIBS_DEP)
+ $(LINK_DLL) $(EXTRA_LIBS)
+endif
+endif
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(COLLATION_DLL_OBJ)
+endif
+ $(RM) $(COLLATION)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
diff --git a/ldap/servers/plugins/collation/collate.c b/ldap/servers/plugins/collation/collate.c
new file mode 100644
index 00000000..603caf53
--- /dev/null
+++ b/ldap/servers/plugins/collation/collate.c
@@ -0,0 +1,454 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* collate.c - implementation of indexing, using a Collation */
+
+#include "collate.h"
+#include <string.h> /* memcpy */
+
+#include <unicode/ucol.h> /* Collation */
+#include <unicode/ucnv.h> /* Conversion */
+#include <unicode/ustring.h> /* UTF8 conversion */
+
+#include <ldap.h> /* LDAP_UTF8LEN */
+#include <slap.h> /* for strcasecmp on non-UNIX platforms and correct debug macro */
+
+void
+collation_init( char *configpath )
+ /* Called once per process, to initialize globals. */
+{
+ /* ICU needs no initialization? */
+}
+
+typedef struct coll_profile_t { /* Collator characteristics */
+ const char* language;
+ const char* country;
+ const char* variant;
+ UColAttributeValue strength; /* one of UCOL_PRIMARY = 0, UCOL_SECONDARY = 1, UCOL_TERTIARY = 2, UCOL_QUATERNARY = 3, UCOL_IDENTICAL = 4 */
+ UColAttributeValue decomposition; /* one of UCOL_OFF = 0, UCOL_DEFAULT = 1, UCOL_ON = 2 */
+} coll_profile_t;
+
+typedef struct coll_id_t { /* associates an OID with a coll_profile_t */
+ char* oid;
+ coll_profile_t* profile;
+} coll_id_t;
+
+/* A list of all OIDs that identify collator profiles: */
+static const coll_id_t** collation_id = NULL;
+static size_t collation_ids = 0;
+
+int
+collation_config (size_t cargc, char** cargv,
+ const char* fname, size_t lineno)
+ /* Process one line from a configuration file.
+ Return 0 if it's OK, -1 if it's not recognized.
+ Any other return value is a process exit code.
+ */
+{
+ if (cargc <= 0) { /* Bizarre. Oh, well... */
+ } else if (!strcasecmp (cargv[0], "NLS")) {
+ /* ignore - not needed anymore with ICU - was used to get path for NLS_Initialize */
+ } else if (!strcasecmp (cargv[0], "collation")) {
+ if ( cargc < 7 ) {
+ LDAPDebug (LDAP_DEBUG_ANY,
+ "%s: line %lu ignored: only %lu arguments (expected "
+ "collation language country variant strength decomposition oid ...)\n",
+ fname, (unsigned long)lineno, (unsigned long)cargc );
+ } else {
+ auto size_t arg;
+ auto coll_profile_t* profile = (coll_profile_t*) slapi_ch_calloc (1, sizeof (coll_profile_t));
+ if (*cargv[1]) profile->language = slapi_ch_strdup (cargv[1]);
+ if (*cargv[2]) profile->country = slapi_ch_strdup (cargv[2]);
+ if (*cargv[3]) profile->variant = slapi_ch_strdup (cargv[3]);
+ switch (atoi(cargv[4])) {
+ case 1: profile->strength = UCOL_PRIMARY; break;
+ case 2: profile->strength = UCOL_SECONDARY; /* no break here? fall through? wtf? */
+ case 3: profile->strength = UCOL_TERTIARY; break;
+ case 4: profile->strength = UCOL_IDENTICAL; break;
+ default: profile->strength = UCOL_SECONDARY;
+ LDAPDebug (LDAP_DEBUG_ANY,
+ "%s: line %lu: strength \"%s\" not supported (will use 2)\n",
+ fname, (unsigned long)lineno, cargv[4]);
+ break;
+ }
+ switch (atoi(cargv[5])) {
+ case 1: profile->decomposition = UCOL_OFF; break;
+ case 2: profile->decomposition = UCOL_DEFAULT; /* no break here? fall through? wtf? */
+ case 3: profile->decomposition = UCOL_ON; break;
+ default: profile->decomposition = UCOL_DEFAULT;
+ LDAPDebug (LDAP_DEBUG_ANY,
+ "%s: line %lu: decomposition \"%s\" not supported (will use 2)\n",
+ fname, (unsigned long)lineno, cargv[5]);
+ break;
+ }
+
+ {
+ char descStr[256];
+ char nameOrder[256];
+ char nameSubstring[256];
+ char oidString[256];
+ char *tmpStr=NULL;
+ Slapi_MatchingRuleEntry *mrentry=slapi_matchingrule_new();
+
+ if(UCOL_PRIMARY == profile->strength) {
+ strcpy(nameOrder,"caseIgnoreOrderingMatch");
+ strcpy(nameSubstring,"caseIgnoreSubstringMatch");
+ }
+ else {
+ strcpy(nameOrder,"caseExactOrderingMatch");
+ strcpy(nameSubstring,"caseExactSubstringMatch");
+ }
+
+ if(cargc > 7) {
+ strcat(nameOrder,"-");
+ strcat(nameOrder,cargv[7]);
+ strcat(nameSubstring,"-");
+ strcat(nameSubstring,cargv[7]);
+ slapi_matchingrule_set(mrentry,SLAPI_MATCHINGRULE_NAME,
+ (void *)slapi_ch_strdup(nameOrder));
+ }
+ else {
+ if(0 != cargv[1][0]) {
+ strcat(nameOrder,"-");
+ strcat(nameSubstring,"-");
+ }
+ strcat(nameOrder,cargv[1]);
+ strcat(nameSubstring,cargv[1]);
+ slapi_matchingrule_set(mrentry,SLAPI_MATCHINGRULE_NAME,
+ (void *)slapi_ch_strdup(nameOrder));
+ }
+ strcpy(oidString,cargv[6]);
+ slapi_matchingrule_set(mrentry,SLAPI_MATCHINGRULE_OID,
+ (void *)slapi_ch_strdup(oidString));
+ if(0 != cargv[2][0]) {
+ sprintf(descStr,"%s-%s",cargv[1],cargv[2]);
+ }
+ else {
+ strcpy(descStr,cargv[1]);
+ }
+ slapi_matchingrule_set(mrentry,SLAPI_MATCHINGRULE_DESC,
+ (void *)slapi_ch_strdup(descStr));
+ slapi_matchingrule_set(mrentry,SLAPI_MATCHINGRULE_SYNTAX,
+ (void *)slapi_ch_strdup(DIRSTRING_SYNTAX_OID));
+ slapi_matchingrule_register(mrentry);
+ slapi_matchingrule_get(mrentry,SLAPI_MATCHINGRULE_NAME,
+ (void *)&tmpStr);
+ slapi_ch_free((void **)&tmpStr);
+ slapi_matchingrule_get(mrentry,SLAPI_MATCHINGRULE_OID,
+ (void *)&tmpStr);
+ slapi_ch_free((void **)&tmpStr);
+ slapi_matchingrule_set(mrentry,SLAPI_MATCHINGRULE_NAME,
+ (void *)slapi_ch_strdup(nameSubstring));
+ strcat(oidString,".6");
+ slapi_matchingrule_set(mrentry,SLAPI_MATCHINGRULE_OID,
+ (void *)slapi_ch_strdup(oidString));
+ slapi_matchingrule_register(mrentry);
+ slapi_matchingrule_free(&mrentry,1);
+ }
+
+
+ for (arg = 6; arg < cargc; ++arg) {
+ auto coll_id_t* id = (coll_id_t*) slapi_ch_malloc (sizeof (coll_id_t));
+ id->oid = slapi_ch_strdup (cargv[arg]);
+ id->profile = profile;
+ if (collation_ids <= 0) {
+ collation_id = (const coll_id_t**) slapi_ch_malloc (2 * sizeof (coll_id_t*));
+ } else {
+ collation_id = (const coll_id_t**) slapi_ch_realloc
+ ((void*)collation_id, (collation_ids + 2) * sizeof (coll_id_t*));
+ }
+ collation_id [collation_ids++] = id;
+ collation_id [collation_ids] = NULL;
+ }
+ }
+ } else {
+ return -1; /* unrecognized */
+ }
+ return 0; /* success */
+}
+
+typedef struct collation_indexer_t
+ /* A kind of indexer, implemented using an ICU Collator */
+{
+ UCollator* collator;
+ UConverter* converter;
+ struct berval** ix_keys;
+ int is_default_collator;
+} collation_indexer_t;
+
+/*
+ Caller must ensure that U == NULL and Ulen == 0 the first time called
+*/
+static UErrorCode
+SetUnicodeStringFromUTF_8 (UChar** U, int32_t* Ulen, int *isAlloced, const struct berval* bv)
+ /* Copy the UTF-8 string bv into the UnicodeString U,
+ but remove leading and trailing whitespace, and
+ convert consecutive whitespaces into a single space.
+ Ulen is set to the number of UChars in the array (not necessarily the number of bytes!)
+ */
+{
+ size_t n;
+ int32_t len = 0; /* length of non-space string */
+ int32_t needLen = 0; /* number of bytes needed for string */
+ UErrorCode err = U_ZERO_ERROR;
+ const char* s = bv->bv_val;
+ const char* begin = NULL; /* will point to beginning of non-space in val */
+ const char* end = NULL; /* will point to the first space after the last non-space char in val */
+ int32_t nUchars = 0;
+
+ if (!bv->bv_len) { /* no value? */
+ return U_INVALID_FORMAT_ERROR; /* don't know what else to use here */
+ }
+
+ /* first, set s to the first non-space char in bv->bv_val */
+ for (n = 0; (n < bv->bv_len) && ldap_utf8isspace((char *)s); ) { /* cast away const */
+ const char *next = LDAP_UTF8NEXT((char *)s); /* cast away const */
+ n += (next - s); /* count bytes, not chars */
+ s = next;
+ }
+ begin = s; /* begin points to first non-space char in val */
+
+ if (n >= bv->bv_len) { /* value is all spaces? */
+ return U_INVALID_FORMAT_ERROR; /* don't know what else to use here */
+ }
+
+ s = bv->bv_val + (bv->bv_len-1); /* move s to last char of bv_val */
+ end = s; /* end points at last char of bv_val - may change below */
+ /* find the last non-null and non-space char of val */
+ for (n = bv->bv_len; (n > 0) && (!*s || ldap_utf8isspace((char *)s));) {
+ const char *prev = LDAP_UTF8PREV((char *)s);
+ end = prev;
+ n -= (s - prev); /* count bytes, not chars */
+ s = prev;
+ }
+
+ /* end now points at last non-null/non-space of val */
+ if (n < 0) { /* bogus */
+ return U_INVALID_FORMAT_ERROR; /* don't know what else to use here */
+ }
+
+ len = LDAP_UTF8NEXT((char *)end) - begin;
+
+ u_strFromUTF8(*U, *Ulen, &nUchars, begin, len, &err);
+ if (nUchars > *Ulen) { /* need more space */
+ if (*isAlloced) { /* realloc space */
+ *U = (UChar *)slapi_ch_realloc((char *)*U, sizeof(UChar) * nUchars);
+ } else { /* must use malloc */
+ *U = (UChar *)slapi_ch_malloc(sizeof(UChar) * nUchars);
+ *isAlloced = 1; /* no longer using fixed buffer */
+ }
+ *Ulen = nUchars;
+ err = U_ZERO_ERROR; /* reset */
+ u_strFromUTF8(*U, *Ulen, NULL, begin, len, &err);
+ } else {
+ *Ulen = nUchars;
+ }
+
+ return err;
+}
+
+static struct berval**
+collation_index (indexer_t* ix, struct berval** bvec, struct berval** prefixes)
+{
+ collation_indexer_t* etc = (collation_indexer_t*) ix->ix_etc;
+ struct berval** keys = NULL;
+ if (bvec) {
+ char keyBuffer[128]; /* try to use static space buffer to avoid malloc */
+ int32_t keyLen = sizeof(keyBuffer);
+ char* key = keyBuffer; /* but key can grow if necessary */
+ size_t keyn = 0;
+ struct berval** bv;
+ UChar charBuffer[128]; /* try to use static space buffer */
+ int32_t nChars = sizeof(charBuffer)/sizeof(UChar); /* but grow if necessary */
+ UChar *chars = charBuffer; /* try to reuse this */
+ int isAlloced = 0; /* using fixed buffer */
+
+ for (bv = bvec; *bv; ++bv) {
+ /* if chars is allocated, nChars will be the capacity and the number of chars in chars */
+ /* otherwise, nChars will be the number of chars, which may be less than the capacity */
+ if (!isAlloced) {
+ nChars = sizeof(charBuffer)/sizeof(UChar); /* reset */
+ }
+ if (U_ZERO_ERROR == SetUnicodeStringFromUTF_8 (&chars, &nChars, &isAlloced, *bv)) {
+ /* nChars is now the number of UChar in chars, which may be less than the
+ capacity of charBuffer if not allocated */
+ struct berval* prefix = prefixes ? prefixes[bv-bvec] : NULL;
+ const size_t prefixLen = prefix ? prefix->bv_len : 0;
+ struct berval* bk = NULL;
+ int32_t realLen; /* real length of key, not keyLen which is buffer size */
+
+ /* try to get the sort key using key and keyLen; only grow key
+ if we need to */
+ /* can use -1 for char len since the conversion from UTF8
+ null terminates the string */
+ realLen = ucol_getSortKey(etc->collator, chars, nChars, (uint8_t *)key, keyLen);
+ if (realLen > keyLen) { /* need more space */
+ if (key == keyBuffer) {
+ key = (char*)slapi_ch_malloc(sizeof(char) * realLen);
+ } else {
+ key = (char*)slapi_ch_realloc(key, sizeof(char) * realLen);
+ }
+ keyLen = ucol_getSortKey(etc->collator, chars, nChars, (uint8_t *)key, realLen);
+ }
+ if (realLen > 0) {
+ bk = (struct berval*) slapi_ch_malloc (sizeof(struct berval));
+
+ bk->bv_len = prefixLen + realLen;
+ bk->bv_val = slapi_ch_malloc (bk->bv_len + 1);
+ if (prefixLen) {
+ memcpy(bk->bv_val, prefix->bv_val, prefixLen);
+ }
+ memcpy(bk->bv_val + prefixLen, key, realLen);
+ bk->bv_val[bk->bv_len] = '\0';
+ LDAPDebug (LDAP_DEBUG_FILTER, "collation_index(%.*s) %lu bytes\n",
+ bk->bv_len, bk->bv_val, (unsigned long)bk->bv_len);
+ keys = (struct berval**)
+ slapi_ch_realloc ((void*)keys, sizeof(struct berval*) * (keyn + 2));
+ keys[keyn++] = bk;
+ keys[keyn] = NULL;
+ }
+ }
+ }
+ if (chars != charBuffer) { /* realloc'ed, need to free */
+ slapi_ch_free((void **)&chars);
+ }
+ if (key != keyBuffer) { /* realloc'ed, need to free */
+ slapi_ch_free_string(&key);
+ }
+ }
+ if (etc->ix_keys != NULL) ber_bvecfree (etc->ix_keys);
+ etc->ix_keys = keys;
+ return keys;
+}
+
+static void
+collation_indexer_destroy (indexer_t* ix)
+ /* The destructor function for a collation-based indexer. */
+{
+ collation_indexer_t* etc = (collation_indexer_t*) ix->ix_etc;
+ if (etc->converter) {
+ ucnv_close(etc->converter);
+ etc->converter = NULL;
+ }
+ if (!etc->is_default_collator) {
+ /* Don't delete the default collation - it seems to cause problems */
+ ucol_close(etc->collator);
+ etc->collator = NULL;
+ }
+ if (etc->ix_keys != NULL) {
+ ber_bvecfree (etc->ix_keys);
+ etc->ix_keys = NULL;
+ }
+ slapi_ch_free((void**)&ix->ix_etc);
+ ix->ix_etc = NULL; /* just for hygiene */
+}
+
+static UErrorCode
+s_newNamedLocaleFromComponents(char **locale, const char *lang, const char *country, const char *variant)
+{
+ UErrorCode err = U_ZERO_ERROR;
+ int hasLang = (lang && *lang);
+ int hasC = (country && *country);
+ int hasVar = (variant && *variant);
+
+ *locale = NULL;
+ if (hasLang) {
+ *locale = PR_smprintf("%s%s%s%s%s", lang, (hasC ? "_" : ""), (hasC ? country : ""),
+ (hasVar ? "_" : ""), (hasVar ? variant : ""));
+ } else {
+ err = U_INVALID_FORMAT_ERROR; /* don't know what else to use here */
+ }
+
+ return err;
+}
+
+indexer_t*
+collation_indexer_create (const char* oid)
+ /* Return a new indexer, based on the collation identified by oid.
+ Return NULL if this can't be done.
+ */
+{
+ indexer_t* ix = NULL;
+ const coll_id_t** id = collation_id;
+ char* locale = NULL; /* NULL == default locale */
+ if (id) for (; *id; ++id) {
+ if (!strcasecmp (oid, (*id)->oid)) {
+ const coll_profile_t* profile = (*id)->profile;
+ const int is_default = (profile->language == NULL &&
+ profile->country == NULL &&
+ profile->variant == NULL);
+ UErrorCode err = U_ZERO_ERROR;
+ if ( ! is_default) {
+ if (locale) {
+ PR_smprintf_free(locale);
+ locale = NULL;
+ }
+ err = s_newNamedLocaleFromComponents(&locale,
+ profile->language,
+ profile->country,
+ profile->variant);
+ }
+ if (err == U_ZERO_ERROR) {
+ UCollator* coll = ucol_open(locale, &err);
+ /*
+ * If we found exactly the right collator for this locale,
+ * or if we found a fallback one, or if we are happy with
+ * the default, use it.
+ */
+ if (err == U_ZERO_ERROR || err == U_USING_FALLBACK_WARNING ||
+ (err == U_USING_DEFAULT_WARNING && is_default)) {
+ collation_indexer_t* etc = (collation_indexer_t*)
+ slapi_ch_calloc (1, sizeof (collation_indexer_t));
+ ix = (indexer_t*) slapi_ch_calloc (1, sizeof (indexer_t));
+ ucol_setAttribute (coll, UCOL_STRENGTH, profile->strength, &err);
+ if (err != U_ZERO_ERROR) {
+ LDAPDebug (LDAP_DEBUG_ANY, "collation_indexer_create: could not "
+ "set the collator strength for oid %s to %d: err %d\n",
+ oid, profile->strength, err);
+ }
+ ucol_setAttribute (coll, UCOL_DECOMPOSITION_MODE, profile->decomposition, &err);
+ if (err != U_ZERO_ERROR) {
+ LDAPDebug (LDAP_DEBUG_ANY, "collation_indexer_create: could not "
+ "set the collator decomposition mode for oid %s to %d: err %d\n",
+ oid, profile->decomposition, err);
+ }
+ etc->collator = coll;
+ etc->is_default_collator = is_default;
+ for (id = collation_id; *id; ++id) {
+ if ((*id)->profile == profile) {
+ break; /* found the 'official' id */
+ }
+ }
+ ix->ix_etc = etc;
+ ix->ix_oid = (*id)->oid;
+ ix->ix_index = collation_index;
+ ix->ix_destroy = collation_indexer_destroy;
+ break; /* return */
+ /* free (etc); */
+ /* free (ix); */
+ } else if (err == U_USING_DEFAULT_WARNING) {
+ LDAPDebug (LDAP_DEBUG_FILTER, "collation_indexer_create: could not "
+ "create an indexer for OID %s for locale %s and could not "
+ "use default locale\n",
+ oid, (locale ? locale : "(default)"), NULL);
+ } else { /* error */
+ LDAPDebug (LDAP_DEBUG_FILTER, "collation_indexer_create: could not "
+ "create an indexer for OID %s for locale %s: err = %d\n",
+ oid, (locale ? locale : "(default)"), err);
+ }
+ if (coll) {
+ ucol_close (coll);
+ coll = NULL;
+ }
+ }
+ break; /* failed to create the specified collator */
+ }
+ }
+ if (locale) {
+ PR_smprintf_free(locale);
+ locale = NULL;
+ }
+ return ix;
+}
diff --git a/ldap/servers/plugins/collation/collate.h b/ldap/servers/plugins/collation/collate.h
new file mode 100644
index 00000000..29e51022
--- /dev/null
+++ b/ldap/servers/plugins/collation/collate.h
@@ -0,0 +1,36 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef _COLLATE_H_
+#define _COLLATE_H_
+
+#include <stddef.h> /* size_t */
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+
+struct indexer_t;
+
+typedef void (*ix_destroy_t) (struct indexer_t*);
+typedef struct berval** (*ix_index_t) (struct indexer_t*, struct berval** values,
+ struct berval** prefixes /* inserted into each key */);
+
+typedef struct indexer_t
+{
+ char* ix_oid;
+ ix_index_t ix_index; /* map values to index keys */
+ ix_destroy_t ix_destroy;
+ void* ix_etc; /* whatever state the implementation needs */
+} indexer_t;
+
+extern void
+collation_init( char *configpath );
+
+extern int
+collation_config (size_t argc, char** argv, const char* fname, size_t lineno);
+
+extern indexer_t*
+collation_indexer_create (const char* oid);
+
+#endif
diff --git a/ldap/servers/plugins/collation/collation.def b/ldap/servers/plugins/collation/collation.def
new file mode 100644
index 00000000..bd5d531b
--- /dev/null
+++ b/ldap/servers/plugins/collation/collation.def
@@ -0,0 +1,10 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Netscape Directory Server 7 Collation Plugin'
+EXPORTS
+ orderingRule_init @2
+ plugin_init_debug_level @3
diff --git a/ldap/servers/plugins/collation/config.c b/ldap/servers/plugins/collation/config.c
new file mode 100644
index 00000000..ef3e66cf
--- /dev/null
+++ b/ldap/servers/plugins/collation/config.c
@@ -0,0 +1,178 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "collate.h"
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include "slap.h"
+
+#define MAXARGS 16
+
+static char *
+strtok_quote( char *line, char *sep )
+{
+ int inquote;
+ char *tmp, *d;
+ static char *next;
+
+ if ( line != NULL ) {
+ next = line;
+ }
+ while ( *next && strchr( sep, *next ) ) {
+ next++;
+ }
+
+ if ( *next == '\0' ) {
+ next = NULL;
+ return( NULL );
+ }
+
+ d = tmp = next;
+ for ( inquote = 0; *next; next++ ) {
+ switch ( *next ) {
+ case '"':
+ if ( inquote ) {
+ inquote = 0;
+ } else {
+ inquote = 1;
+ }
+ break;
+
+#ifndef _WIN32
+ case '\\':
+ *d++ = *++next;
+ break;
+#endif
+
+ default:
+ if ( ! inquote ) {
+ if ( strchr( sep, *next ) != NULL ) {
+ *d++ = '\0';
+ next++;
+ return( tmp );
+ }
+ }
+ *d++ = *next;
+ break;
+ }
+ }
+ *d = '\0';
+
+ return( tmp );
+}
+
+static void
+fp_parse_line(
+ char *line,
+ int *argcp,
+ char **argv
+)
+{
+ char * token;
+
+ *argcp = 0;
+ for ( token = strtok_quote( line, " \t" ); token != NULL;
+ token = strtok_quote( NULL, " \t" ) ) {
+ if ( *argcp == MAXARGS ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Too many tokens (max %d)\n",
+ MAXARGS, 0, 0 );
+ exit( 1 );
+ }
+ argv[(*argcp)++] = token;
+ }
+ argv[*argcp] = NULL;
+}
+
+static char buf[BUFSIZ];
+static char *line;
+static int lmax, lcur;
+
+static void
+fp_getline_init( int *lineno )
+{
+ *lineno = -1;
+ buf[0] = '\0';
+}
+
+#define CATLINE( buf ) { \
+ int len; \
+ len = strlen( buf ); \
+ while ( lcur + len + 1 > lmax ) { \
+ lmax += BUFSIZ; \
+ line = (char *) slapi_ch_realloc( line, lmax ); \
+ } \
+ strcpy( line + lcur, buf ); \
+ lcur += len; \
+}
+
+static char *
+fp_getline( FILE *fp, int *lineno )
+{
+ char *p;
+
+ lcur = 0;
+ CATLINE( buf );
+ (*lineno)++;
+
+ /* hack attack - keeps us from having to keep a stack of bufs... */
+ if ( strncasecmp( line, "include", 7 ) == 0 ) {
+ buf[0] = '\0';
+ return( line );
+ }
+
+ while ( fgets( buf, sizeof(buf), fp ) != NULL ) {
+ if ( (p = strchr( buf, '\n' )) != NULL ) {
+ *p = '\0';
+ }
+ if ( ! isspace( buf[0] ) ) {
+ return( line );
+ }
+
+ CATLINE( buf );
+ (*lineno)++;
+ }
+ buf[0] = '\0';
+
+ return( line[0] ? line : NULL );
+}
+
+void
+collation_read_config( char *fname )
+{
+ FILE* fp;
+ char* line;
+ int cargc;
+ char* cargv[MAXARGS];
+ int lineno;
+
+ fp = fopen( fname, "r" );
+ if ( fp == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "could not open config file \"%s\" - absolute path?\n",
+ fname, 0, 0 );
+ return; /* Do not exit */
+ }
+
+ LDAPDebug( LDAP_DEBUG_CONFIG, "reading config file %s\n", fname, 0, 0 );
+
+ fp_getline_init( &lineno );
+ while ( (line = fp_getline( fp, &lineno )) != NULL ) {
+ /* skip comments and blank lines */
+ if ( line[0] == '#' || line[0] == '\0' ) {
+ continue;
+ }
+ LDAPDebug( LDAP_DEBUG_CONFIG, "line %d: %s\n", lineno, line, 0 );
+ fp_parse_line( line, &cargc, cargv );
+ if ( cargc < 1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "%s: line %d: bad config line (ignored)\n",
+ fname, lineno, 0 );
+ continue;
+ }
+ collation_config (cargc, cargv, fname, lineno);
+ }
+ fclose(fp);
+}
diff --git a/ldap/servers/plugins/collation/config.h b/ldap/servers/plugins/collation/config.h
new file mode 100644
index 00000000..1a4aef66
--- /dev/null
+++ b/ldap/servers/plugins/collation/config.h
@@ -0,0 +1,12 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef _COLL_CONFIG_H_
+#define _COLL_CONFIG_H_
+
+extern void
+collation_read_config( char *fname );
+
+#endif
diff --git a/ldap/servers/plugins/collation/debug.c b/ldap/servers/plugins/collation/debug.c
new file mode 100644
index 00000000..99266a33
--- /dev/null
+++ b/ldap/servers/plugins/collation/debug.c
@@ -0,0 +1,14 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void
+plugin_init_debug_level (int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
diff --git a/ldap/servers/plugins/collation/dllmain.c b/ldap/servers/plugins/collation/dllmain.c
new file mode 100644
index 00000000..fa158500
--- /dev/null
+++ b/ldap/servers/plugins/collation/dllmain.c
@@ -0,0 +1,129 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Microsoft Windows specifics for collation DLL
+ */
+#include "ldap.h"
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
+
+#ifdef LDAP_DEBUG
+#ifndef _WIN32
+#include <stdarg.h>
+#include <stdio.h>
+
+void LDAPDebug( int level, char* fmt, ... )
+{
+ static char debugBuf[1024];
+
+ if (module_ldap_debug && (*module_ldap_debug & level))
+ {
+ va_list ap;
+ va_start (ap, fmt);
+ _snprintf (debugBuf, sizeof(debugBuf), fmt, ap);
+ va_end (ap);
+
+ OutputDebugString (debugBuf);
+ }
+}
+#endif
+#endif
+
+#ifndef _WIN32
+/* The 16-bit version of the RTL does not implement perror() */
+#include <stdio.h>
+
+void perror( const char *msg )
+{
+ char buf[128];
+ wsprintf( buf, "%s: error %d\n", msg, WSAGetLastError()) ;
+ OutputDebugString( buf );
+}
+
+#endif
diff --git a/ldap/servers/plugins/collation/orfilter.c b/ldap/servers/plugins/collation/orfilter.c
new file mode 100644
index 00000000..65222baa
--- /dev/null
+++ b/ldap/servers/plugins/collation/orfilter.c
@@ -0,0 +1,984 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* orfilter.c - implementation of ordering rule filter */
+
+#include <ldap.h> /* LDAP_UTF8INC */
+#include <slap.h> /* for debug macros */
+#include <slapi-plugin.h> /* slapi_berval_cmp, SLAPI_BERVAL_EQ */
+#include "collate.h" /* indexer_t, collation_xxx */
+#include "config.h" /* collation_read_config */
+#include "orfilter.h"
+
+#ifdef HPUX11
+#include <dl.h>
+#endif /* HPUX11 */
+
+static indexer_t*
+indexer_create (const char* oid)
+{
+ return collation_indexer_create (oid);
+}
+
+static void
+indexer_free (indexer_t* ix)
+{
+ if (ix->ix_destroy != NULL) {
+ ix->ix_destroy (ix);
+ }
+ slapi_ch_free((void**)&ix);
+}
+
+typedef struct or_filter_t {
+ /* implements a filter, using an indexer */
+ char* or_type;
+ int or_op; /* LDAPI_OP_xxx */
+ char* or_oid;
+ struct berval** or_values;
+ struct berval** or_match_keys;
+ struct berval** or_index_keys;
+ indexer_t* or_indexer; /* used to construct or_match_keys and or_index_keys */
+} or_filter_t;
+
+static or_filter_t*
+or_filter_get (Slapi_PBlock* pb)
+{
+ auto void* obj = NULL;
+ if ( ! slapi_pblock_get (pb, SLAPI_PLUGIN_OBJECT, &obj)) {
+ return (or_filter_t*)obj;
+ }
+ return NULL;
+}
+
+static int
+or_filter_destroy (Slapi_PBlock* pb)
+{
+ auto or_filter_t* or = or_filter_get (pb);
+ LDAPDebug (LDAP_DEBUG_FILTER, "or_filter_destroy(%p)\n", (void*)or, 0, 0);
+ if (or != NULL) {
+ slapi_ch_free((void**)&or->or_type);
+ slapi_ch_free((void**)&or->or_oid);
+ if (or->or_values != NULL) {
+ ber_bvecfree (or->or_values);
+ or->or_values = NULL;
+ }
+ if (or->or_match_keys != NULL) {
+ ber_bvecfree (or->or_match_keys);
+ or->or_match_keys = NULL;
+ }
+ if (or->or_index_keys != NULL) {
+ ber_bvecfree (or->or_index_keys);
+ or->or_index_keys = NULL;
+ }
+ if (or->or_indexer != NULL) {
+ indexer_free (or->or_indexer);
+ or->or_indexer = NULL;
+ }
+ slapi_ch_free((void**)&or);
+ }
+ return 0;
+}
+
+#define MAX_CHAR_COMBINING 3
+/* The maximum number of Unicode characters that may combine
+ to form a single collation element.
+*/
+
+static int
+ss_match (struct berval* value,
+ const struct berval* key0,
+ indexer_t* ix)
+/* returns: 0 a prefix of value matched key
+ * 1 a subsequent substring might match; try again
+ * -1 nothing in value will match; give up
+ */
+{
+ auto struct berval* vals[2];
+ auto struct berval val;
+ auto struct berval key;
+ auto size_t attempts = MAX_CHAR_COMBINING;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+ val.bv_val = value->bv_val;
+ val.bv_len = 0;
+ key.bv_val = key0->bv_val;
+ key.bv_len = key0->bv_len - 1;
+ while (1) {
+ auto struct berval** vkeys = ix->ix_index (ix, vals, NULL);
+ if (vkeys && vkeys[0]) {
+ auto const struct berval* vkey = vkeys[0];
+ if (vkey->bv_len > key.bv_len) {
+ if (--attempts <= 0) {
+ break; /* No match at this starting point */
+ } /* else Try looking at another character;
+ it may combine, and produce a shorter key.
+ */
+ } else if (SLAPI_BERVAL_EQ (vkey, &key)) {
+ value->bv_len -= val.bv_len;
+ value->bv_val += val.bv_len;
+ return 0;
+ }
+ }
+ if (val.bv_len >= value->bv_len) {
+ break;
+ }
+ val.bv_len += LDAP_UTF8LEN (val.bv_val + val.bv_len);
+ }
+ if (value->bv_len > 0) {
+ auto size_t one = LDAP_UTF8LEN (value->bv_val);
+ value->bv_len -= one;
+ value->bv_val += one;
+ return 1;
+ }
+ return -1;
+}
+
+static int
+ss_filter_match (or_filter_t* or, struct berval** vals)
+/* returns: 0 filter matched
+ * -1 filter did not match
+ * >0 an LDAP error code
+ */
+{
+ auto int rc = -1; /* no match */
+ auto indexer_t* ix = or->or_indexer;
+ if (vals != NULL) for (; *vals; ++vals) {
+ auto struct berval v;
+ auto struct berval** k = or->or_match_keys;
+ if (k == NULL || *k == NULL) {
+ rc = 0; /* present */
+ break;
+ }
+ v.bv_len = (*vals)->bv_len;
+ v.bv_val = (*vals)->bv_val;
+ if ((*k)->bv_len > 0 && ss_match (&v, *k, ix) != 0) {
+ break; /* initial failed */
+ }
+ rc = 0; /* so far, so good */
+ while (*++k) {
+ if ((*k)->bv_len <= 0) {
+ rc = 0;
+ } else if (k[1]) { /* middle */
+ do {
+ rc = ss_match (&v, *k, ix);
+ } while (rc > 0);
+ if (rc < 0) {
+ break;
+ }
+ } else { /* final */
+ auto size_t attempts = MAX_CHAR_COMBINING;
+ auto char* limit = v.bv_val;
+ auto struct berval** vkeys;
+ auto struct berval* vals[2];
+ auto struct berval key;
+ rc = -1;
+ vals[0] = &v;
+ vals[1] = NULL;
+ key.bv_val = (*k)->bv_val;
+ key.bv_len = (*k)->bv_len - 1;
+ v.bv_val = (*vals)->bv_val + (*vals)->bv_len;
+ while(1) {
+ v.bv_len = (*vals)->bv_len - (v.bv_val - (*vals)->bv_val);
+ vkeys = ix->ix_index (ix, vals, NULL);
+ if (vkeys && vkeys[0]) {
+ auto const struct berval* vkey = vkeys[0];
+ if (vkey->bv_len > key.bv_len) {
+ if (--attempts <= 0) {
+ break;
+ } /* else Try looking at another character;
+ it may combine, and produce a shorter key.
+ */
+ } else if (SLAPI_BERVAL_EQ (vkey, &key)) {
+ rc = 0;
+ break;
+ }
+ }
+ if (v.bv_val <= limit) break;
+ LDAP_UTF8DEC (v.bv_val);
+ }
+ break;
+ }
+ }
+ if (rc != -1) break;
+ }
+ return rc;
+}
+
+static int
+op_filter_match (or_filter_t* or, struct berval** vals)
+{
+ auto indexer_t* ix = or->or_indexer;
+ auto struct berval** v = ix->ix_index (ix, vals, NULL);
+ if (v != NULL) for (; *v; ++v) {
+ auto struct berval** k = or->or_match_keys;
+ if (k != NULL) for (; *k; ++k) {
+ switch (or->or_op) {
+ case SLAPI_OP_LESS:
+ if (slapi_berval_cmp (*v, *k) < 0) return 0; break;
+ case SLAPI_OP_LESS_OR_EQUAL:
+ if (slapi_berval_cmp (*v, *k) <= 0) return 0; break;
+ case SLAPI_OP_EQUAL:
+ if (SLAPI_BERVAL_EQ (*v, *k)) return 0; break;
+ case SLAPI_OP_GREATER_OR_EQUAL:
+ if (slapi_berval_cmp (*v, *k) >= 0) return 0; break;
+ case SLAPI_OP_GREATER:
+ if (slapi_berval_cmp (*v, *k) > 0) return 0; break;
+ default:
+ break;
+ }
+ }
+ }
+ return -1;
+}
+
+static int
+or_filter_match (void* obj, Slapi_Entry* entry, Slapi_Attr* attr)
+/* returns: 0 filter matched
+ * -1 filter did not match
+ * >0 an LDAP error code
+ */
+{
+ auto int rc = -1; /* no match */
+ auto or_filter_t* or = (or_filter_t*)obj;
+ for (; attr != NULL; slapi_entry_next_attr (entry, attr, &attr)) {
+ auto char* type = NULL;
+ auto struct berval** vals = NULL;
+
+/*
+ * XXXmcs 1-March-2001: This code would perform better if it did not make
+ * a copy of the values here, but that would require re-writing the code
+ * in this file to use Slapi_ValueSet's instead of struct berval **'s
+ * (and that is not a small project).
+ */
+ if (!slapi_attr_get_type (attr, &type) && type != NULL &&
+ !slapi_attr_type_cmp (or->or_type, type, 2/*match subtypes*/) &&
+ !slapi_attr_get_bervals_copy(attr, &vals) && vals != NULL) {
+
+ if (or->or_op == SLAPI_OP_SUBSTRING) {
+ rc = ss_filter_match (or, vals);
+ } else {
+ rc = op_filter_match (or, vals);
+ }
+
+ ber_bvecfree( vals );
+ vals = NULL;
+ if (rc >= 0) break;
+ }
+ }
+ return rc;
+}
+
+#define WILDCARD '*'
+/* If you want a filter value to contain a non-wildcard '*' or '\'
+ you write "\2a" or "\5c" (the ASCII codes, in hexadecimal).
+ For example, "4\2a4*flim\5cflam"
+ matches a value that begins with "4*4" and ends with "flim\flam"
+ (except that all the "\" should be doubled in C string literals).
+ This conforms to <draft-ietf-asid-ldapv3-attributes-08> section 8.3.
+*/
+
+static void
+ss_unescape (struct berval* val)
+{
+ char* s = val->bv_val;
+ char* t = s;
+ char* limit = s + val->bv_len;
+ while (s < limit) {
+ if (!memcmp (s, "\\2a", 3) ||
+ !memcmp (s, "\\2A", 3)) {
+ *t++ = WILDCARD;
+ s += 3;
+ } else if (!memcmp (s, "\\5c", 3) ||
+ !memcmp (s, "\\5C", 3)) {
+ *t++ = '\\';
+ s += 3;
+ } else {
+ if (t == s) LDAP_UTF8INC (t);
+ else t += LDAP_UTF8COPY (t, s);
+ LDAP_UTF8INC (s);
+ }
+ }
+ val->bv_len = t - val->bv_val;
+}
+
+static struct berval*
+slapi_ch_bvdup0 (struct berval* val)
+ /* Return a copy of val, with a 0 byte following the end. */
+{
+ auto struct berval* result = (struct berval*)
+ slapi_ch_malloc (sizeof (struct berval));
+ result->bv_len = val->bv_len;
+ result->bv_val = slapi_ch_malloc (result->bv_len + 1);
+ if (result->bv_len > 0) {
+ memcpy (result->bv_val, val->bv_val, result->bv_len);
+ }
+ result->bv_val[result->bv_len] = '\0';
+ return result;
+}
+
+static struct berval*
+ss_filter_value (const char* s, const size_t len, struct berval* val)
+{
+ val->bv_len = len;
+ if (len > 0) memcpy (val->bv_val, s, len);
+ ss_unescape (val);
+ return slapi_ch_bvdup0 (val);
+}
+
+static struct berval**
+ss_filter_values (struct berval* pattern, int* query_op)
+ /* Split the pattern into its substrings and return them. */
+{
+ auto struct berval** result;
+ auto struct berval val;
+ auto size_t n;
+ auto char* s;
+ auto char* p;
+ auto char* plimit = pattern->bv_val + pattern->bv_len;
+
+ /* Compute the length of the result array, and
+ the maximum bv_len of any of its elements. */
+ val.bv_len = 0;
+ n = 2; /* one key, plus NULL terminator */
+ s = pattern->bv_val;
+ for (p = s; p < plimit; LDAP_UTF8INC(p)) {
+ switch (*p) {
+ case WILDCARD:
+ ++n;
+ {
+ auto const size_t len = (p - s);
+ if (val.bv_len < len)
+ val.bv_len = len;
+ }
+ while (++p != plimit && *p == WILDCARD);
+ s = p;
+ break;
+ default: break;
+ }
+ }
+ if (n == 2) { /* no wildcards in pattern */
+ auto struct berval** pvec = (struct berval**) slapi_ch_malloc (sizeof (struct berval*) * 2);
+ auto struct berval* pv = (struct berval*) slapi_ch_malloc (sizeof (struct berval));
+ pvec[0] = pv;
+ pvec[1] = NULL;
+ pv->bv_len = pattern->bv_len;
+ pv->bv_val = slapi_ch_malloc (pv->bv_len);
+ memcpy (pv->bv_val, pattern->bv_val, pv->bv_len);
+ ss_unescape (pv);
+ *query_op = SLAPI_OP_EQUAL;
+ return pvec;
+ } else if (n == 3 && pattern->bv_len <= 1) { /* entire pattern is one wildcard */
+ return NULL; /* presence */
+ }
+ {
+ auto const size_t len = (p - s);
+ if (val.bv_len < len)
+ val.bv_len = len;
+ }
+ result = (struct berval**) slapi_ch_malloc (n * sizeof (struct berval*));
+ val.bv_val = slapi_ch_malloc (val.bv_len);
+ n = 0;
+ s = pattern->bv_val;
+ for (p = s; p < plimit; LDAP_UTF8INC(p)) {
+ switch (*p) {
+ case WILDCARD:
+ result[n++] = ss_filter_value (s, p-s, &val);
+ while (++p != plimit && *p == WILDCARD);
+ s = p;
+ break;
+ default: break;
+ }
+ }
+ if (p != s || s == plimit) {
+ result[n++] = ss_filter_value (s, p-s, &val);
+ }
+ result[n] = NULL;
+ slapi_ch_free((void**)&val.bv_val);
+ return result;
+}
+
+static struct berval*
+ss_filter_key (indexer_t* ix, struct berval* val)
+{
+ struct berval* key = (struct berval*) slapi_ch_calloc (1, sizeof(struct berval));
+ if (val->bv_len > 0) {
+ struct berval** keys = NULL;
+ auto struct berval* vals[2];
+ vals[0] = val;
+ vals[1] = NULL;
+ keys = ix->ix_index (ix, vals, NULL);
+ if (keys && keys[0]) {
+ key->bv_len = keys[0]->bv_len + 1;
+ key->bv_val = slapi_ch_malloc (key->bv_len);
+ memcpy (key->bv_val, keys[0]->bv_val, keys[0]->bv_len);
+ key->bv_val[key->bv_len-1] = '\0';
+ }
+ }
+ return key;
+}
+
+static struct berval**
+ss_filter_keys (indexer_t* ix, struct berval** values)
+ /* Index the substrings and return the key values,
+ with an extra byte appended to each key, so that
+ an empty key definitely implies an absent value.
+ */
+{
+ auto struct berval** keys = NULL;
+ if (values != NULL) {
+ auto size_t n; /* how many substring values */
+ auto struct berval** val;
+ for (n=0,val=values; *val != NULL; ++n,++val);
+ keys = (struct berval**) slapi_ch_malloc ((n+1) * sizeof (struct berval*));
+ for (n=0,val=values; *val != NULL; ++n,++val) {
+ keys[n] = ss_filter_key (ix, *val);
+ }
+ keys[n] = NULL;
+ }
+ return keys;
+}
+
+static int or_filter_index (Slapi_PBlock* pb);
+
+static int
+or_filter_create (Slapi_PBlock* pb)
+{
+ auto int rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION; /* failed to initialize */
+ auto char* mrOID = NULL;
+ auto char* mrTYPE = NULL;
+ auto struct berval* mrVALUE = NULL;
+ auto or_filter_t* or = NULL;
+
+ if (!slapi_pblock_get (pb, SLAPI_PLUGIN_MR_OID, &mrOID) && mrOID != NULL &&
+ !slapi_pblock_get (pb, SLAPI_PLUGIN_MR_TYPE, &mrTYPE) && mrTYPE != NULL &&
+ !slapi_pblock_get (pb, SLAPI_PLUGIN_MR_VALUE, &mrVALUE) && mrVALUE != NULL) {
+ auto size_t len = mrVALUE->bv_len;
+ auto indexer_t* ix = NULL;
+ auto int op = SLAPI_OP_EQUAL;
+ auto struct berval bv;
+ auto int reusable = MRF_ANY_TYPE;
+
+ LDAPDebug (LDAP_DEBUG_FILTER, "=> or_filter_create(oid %s; type %s)\n",
+ mrOID, mrTYPE, 0);
+ if (len > 1 && (ix = indexer_create (mrOID)) != NULL) {
+ auto char* val = mrVALUE->bv_val;
+ switch (val[0]) {
+ case '=': break;
+ case '<': op = (val[1] == '=') ?
+ SLAPI_OP_LESS_OR_EQUAL : SLAPI_OP_LESS; break;
+ case '>': op = (val[1] == '=') ?
+ SLAPI_OP_GREATER_OR_EQUAL : SLAPI_OP_GREATER; break;
+ case WILDCARD: op = SLAPI_OP_SUBSTRING; break;
+ default:
+ break;
+ }
+ for (; len > 0 && *val != ' '; ++val, --len);
+ if (len > 0) ++val, --len; /* skip the space */
+ bv.bv_len = len;
+ bv.bv_val = (len > 0) ? val : NULL;
+ } else { /* mrOID does not identify an ordering rule. */
+ /* Is it an ordering rule OID with a relational operator suffix? */
+ auto size_t oidlen = strlen (mrOID);
+ if (oidlen > 2 && mrOID[oidlen-2] == '.') {
+ op = atoi (mrOID + oidlen - 1);
+ switch (op) {
+ case SLAPI_OP_LESS:
+ case SLAPI_OP_LESS_OR_EQUAL:
+ case SLAPI_OP_EQUAL:
+ case SLAPI_OP_GREATER_OR_EQUAL:
+ case SLAPI_OP_GREATER:
+ case SLAPI_OP_SUBSTRING:
+ {
+ auto char* or_oid = slapi_ch_strdup (mrOID);
+ or_oid [oidlen-2] = '\0';
+ ix = indexer_create (or_oid);
+ if (ix != NULL) {
+ memcpy (&bv, mrVALUE, sizeof(struct berval));
+ reusable |= MRF_ANY_VALUE;
+ }
+ slapi_ch_free((void**)&or_oid);
+ }
+ break;
+ default: /* not a relational operator */
+ break;
+ }
+ }
+ }
+ if (ix != NULL) {
+ or = (or_filter_t*) slapi_ch_calloc (1, sizeof (or_filter_t));
+ or->or_type = slapi_ch_strdup (mrTYPE);
+ or->or_indexer = ix;
+ or->or_op = op;
+ if (op == SLAPI_OP_SUBSTRING) {
+ or->or_values = ss_filter_values (&bv, &(or->or_op));
+ } else {
+ or->or_values = (struct berval**)
+ slapi_ch_malloc (2 * sizeof (struct berval*));
+ or->or_values[0] = slapi_ch_bvdup0 (&bv);
+ or->or_values[1] = NULL;
+ }
+ {
+ auto struct berval** val = or->or_values;
+ if (val) for (; *val; ++val) {
+ LDAPDebug (LDAP_DEBUG_FILTER, "value \"%s\"\n", (*val)->bv_val, 0, 0);
+ }
+ }
+ if (or->or_op == SLAPI_OP_SUBSTRING) {
+ or->or_match_keys = ss_filter_keys (ix, or->or_values);
+ } else {
+ or->or_match_keys = slapi_ch_bvecdup (
+ ix->ix_index (ix, or->or_values, NULL));
+ }
+ slapi_pblock_set (pb, SLAPI_PLUGIN_OBJECT, or);
+ slapi_pblock_set (pb, SLAPI_PLUGIN_DESTROY_FN, (void*)or_filter_destroy);
+ slapi_pblock_set (pb, SLAPI_PLUGIN_MR_FILTER_MATCH_FN, (void*)or_filter_match);
+ slapi_pblock_set (pb, SLAPI_PLUGIN_MR_FILTER_INDEX_FN, (void*)or_filter_index);
+/* slapi_pblock_set (pb, SLAPI_PLUGIN_MR_FILTER_REUSABLE, &reusable); */
+/* slapi_pblock_set (pb, SLAPI_PLUGIN_MR_FILTER_RESET_FN, ?); to be implemented */
+ rc = LDAP_SUCCESS;
+ }
+ } else {
+ LDAPDebug (LDAP_DEBUG_FILTER, "=> or_filter_create missing parameter(s)\n", 0, 0, 0);
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "<= or_filter_create(%p) %i\n", (void*)or, rc, 0);
+ return rc;
+}
+
+static indexer_t*
+op_indexer_get (Slapi_PBlock* pb)
+{
+ auto void* obj = NULL;
+ if ( ! slapi_pblock_get (pb, SLAPI_PLUGIN_OBJECT, &obj)) {
+ return (indexer_t*)obj;
+ }
+ return NULL;
+}
+
+static int
+op_indexer_destroy (Slapi_PBlock* pb)
+{
+ auto indexer_t* ix = op_indexer_get (pb);
+ LDAPDebug (LDAP_DEBUG_FILTER, "op_indexer_destroy(%p)\n", (void*)ix, 0, 0);
+ if (ix != NULL) {
+ indexer_free (ix);
+ }
+ return 0;
+}
+
+static int
+op_index_entry (Slapi_PBlock* pb)
+ /* Compute collation keys (when writing an entry). */
+{
+ auto indexer_t* ix = op_indexer_get (pb);
+ auto int rc;
+ struct berval** values;
+ if (ix != NULL && ix->ix_index != NULL &&
+ !slapi_pblock_get (pb, SLAPI_PLUGIN_MR_VALUES, &values) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_MR_KEYS, ix->ix_index (ix, values, NULL))) {
+ rc = 0;
+ } else {
+ rc = LDAP_OPERATIONS_ERROR;
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "op_index_entry(%p) %i\n", (void*)ix, rc, 0);
+ return rc;
+}
+
+static int
+op_index_search (Slapi_PBlock* pb)
+ /* Compute collation keys (when searching for entries). */
+{
+ auto or_filter_t* or = or_filter_get (pb);
+ auto int rc = LDAP_OPERATIONS_ERROR;
+ if (or != NULL) {
+ auto indexer_t* ix = or->or_indexer;
+ struct berval** values;
+ if (or->or_index_keys == NULL && ix != NULL && ix->ix_index != NULL &&
+ !slapi_pblock_get (pb, SLAPI_PLUGIN_MR_VALUES, &values)) {
+ or->or_index_keys = slapi_ch_bvecdup (
+ ix->ix_index (ix, values, NULL));
+ }
+ if (or->or_index_keys) {
+ rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_KEYS, or->or_index_keys);
+ }
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "op_index_search(%p) %i\n", (void*)or, rc, 0);
+ return rc;
+}
+
+typedef struct ss_indexer_t {
+ char* ss_oid; /* ss_indexer->ix_oid && ".6" */
+ indexer_t* ss_indexer;
+} ss_indexer_t;
+
+static void
+ss_indexer_free (ss_indexer_t* ss)
+{
+ slapi_ch_free((void**)&ss->ss_oid);
+ if (ss->ss_indexer != NULL) {
+ indexer_free (ss->ss_indexer);
+ ss->ss_indexer = NULL;
+ }
+ slapi_ch_free((void**)&ss);
+}
+
+static ss_indexer_t*
+ss_indexer_get (Slapi_PBlock* pb)
+{
+ auto void* obj = NULL;
+ if ( ! slapi_pblock_get (pb, SLAPI_PLUGIN_OBJECT, &obj)) {
+ return (ss_indexer_t*)obj;
+ }
+ return NULL;
+}
+
+static void
+ss_indexer_destroy (Slapi_PBlock* pb)
+{
+ auto ss_indexer_t* ss = ss_indexer_get (pb);
+ LDAPDebug (LDAP_DEBUG_FILTER, "ss_indexer_destroy(%p)\n", (void*)ss, 0, 0);
+ if (ss) {
+ ss_indexer_free (ss);
+ }
+}
+
+#define SS_INDEX_LENGTH 3 /* characters */
+
+static char ss_prefixI = '[';
+static char ss_prefixM = '|';
+static char ss_prefixF = '}';
+
+static struct berval ss_index_initial = {1, &ss_prefixI};
+static struct berval ss_index_middle = {1, &ss_prefixM};
+static struct berval ss_index_final = {1, &ss_prefixF};
+
+static int
+long_enough (struct berval* bval, size_t enough)
+{
+ if (bval) {
+ auto size_t len = 0;
+ auto char* next = bval->bv_val;
+ auto char* last = next + bval->bv_len;
+ while (next < last) {
+ LDAP_UTF8INC (next);
+ if (++len >= enough) {
+ if (next > last) next = last;
+ bval->bv_len = next - bval->bv_val;
+ return 1;
+ }
+ }
+ }
+ return !enough;
+}
+
+static int
+ss_index_entry (Slapi_PBlock* pb)
+ /* Compute substring index keys (when writing an entry). */
+{
+ auto int rc = LDAP_OPERATIONS_ERROR;
+ auto size_t substringsLen = 0;
+ struct berval** values;
+ auto ss_indexer_t* ss = ss_indexer_get (pb);
+ auto indexer_t* ix = ss ? ss->ss_indexer : NULL;
+ if (ix != NULL && ix->ix_index != NULL &&
+ !slapi_pblock_get (pb, SLAPI_PLUGIN_MR_VALUES, &values)) {
+ auto struct berval* substrings = NULL;
+ auto struct berval** prefixes = NULL;
+ auto struct berval** value;
+ for (value = values; *value != NULL; ++value) {
+ auto struct berval substring;
+ substring.bv_val = (*value)->bv_val;
+ substring.bv_len = (*value)->bv_len;
+ if (long_enough (&substring, SS_INDEX_LENGTH-1)) {
+ auto struct berval* prefix = &ss_index_initial;
+ auto size_t offset;
+ for (offset = 0; 1; ++offset) {
+ ++substringsLen;
+ substrings = (struct berval*)
+ slapi_ch_realloc ((void*)substrings, substringsLen * sizeof(struct berval));
+ memcpy (&(substrings[substringsLen-1]), &substring, sizeof (struct berval));
+ prefixes = (struct berval**)
+ slapi_ch_realloc ((void*)prefixes, substringsLen * sizeof(struct berval*));
+ prefixes[substringsLen-1] = prefix;
+
+ if (offset != 0) LDAP_UTF8INC (substring.bv_val);
+ substring.bv_len = (*value)->bv_len - (substring.bv_val - (*value)->bv_val);
+ if (long_enough (&substring, SS_INDEX_LENGTH)) {
+ prefix = &ss_index_middle;
+ } else if (long_enough (&substring, SS_INDEX_LENGTH-1)) {
+ prefix = &ss_index_final;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ if (substrings != NULL) {
+ auto struct berval** vector = (struct berval**)
+ slapi_ch_malloc ((substringsLen+1) * sizeof(struct berval*));
+ auto size_t i;
+ for (i = 0; i < substringsLen; ++i) vector[i] = &(substrings[i]);
+ vector[substringsLen] = NULL;
+ rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_KEYS, ix->ix_index (ix, vector, prefixes));
+ slapi_ch_free((void**)&vector);
+ slapi_ch_free((void**)&substrings);
+ slapi_ch_free((void**)&prefixes);
+ }
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "ss_index_entry(%p) %i %lu substrings\n",
+ (void*)ss, rc, (unsigned long)substringsLen);
+ return rc;
+}
+
+static int
+ss_index_search (Slapi_PBlock* pb)
+ /* Compute substring search keys (when searching for entries). */
+{
+ auto int rc = LDAP_OPERATIONS_ERROR;
+ auto or_filter_t* or = or_filter_get (pb);
+ if (or) {
+ if (or->or_index_keys == NULL /* not yet computed */ &&
+ or->or_values && or->or_indexer && or->or_indexer->ix_index) {
+ auto size_t substringsLen = 0;
+ auto struct berval* substrings = NULL;
+ auto struct berval** prefixes = NULL;
+ auto struct berval** value;
+ for (value = or->or_values; *value != NULL; ++value) {
+ auto size_t offset;
+ auto struct berval substring;
+ substring.bv_val = (*value)->bv_val;
+ for (offset = 0; 1; ++offset, LDAP_UTF8INC (substring.bv_val)) {
+ auto struct berval* prefix = NULL;
+ substring.bv_len = (*value)->bv_len - (substring.bv_val - (*value)->bv_val);
+ if (offset == 0 && value == or->or_values) {
+ if (long_enough (&substring, SS_INDEX_LENGTH - 1)) {
+ prefix = &ss_index_initial;
+ }
+ } else if (value[1] != NULL) {
+ if (long_enough (&substring, SS_INDEX_LENGTH)) {
+ prefix = &ss_index_middle;
+ }
+ } else if (long_enough (&substring, SS_INDEX_LENGTH)) {
+ prefix = &ss_index_middle;
+ } else if (long_enough (&substring, SS_INDEX_LENGTH-1)) {
+ prefix = &ss_index_final;
+ }
+ if (prefix == NULL) break;
+ ++substringsLen;
+ substrings = (struct berval*)
+ slapi_ch_realloc ((void*)substrings, substringsLen * sizeof(struct berval));
+ memcpy (&(substrings[substringsLen-1]), &substring, sizeof (struct berval));
+ prefixes = (struct berval**)
+ slapi_ch_realloc ((void*)prefixes, substringsLen * sizeof(struct berval*));
+ prefixes[substringsLen-1] = prefix;
+ }
+ }
+ if (substrings != NULL) {
+ auto indexer_t* ix = or->or_indexer;
+ auto struct berval** vector = (struct berval**)
+ slapi_ch_malloc ((substringsLen+1) * sizeof(struct berval*));
+ auto size_t i;
+ for (i = 0; i < substringsLen; ++i) vector[i] = &(substrings[i]);
+ vector[substringsLen] = NULL;
+ or->or_index_keys = slapi_ch_bvecdup (
+ ix->ix_index (ix, vector, prefixes));
+ slapi_ch_free((void**)&vector);
+ slapi_ch_free((void**)&substrings);
+ slapi_ch_free((void**)&prefixes);
+ }
+ }
+ if (or->or_index_keys) {
+ rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_KEYS, or->or_index_keys);
+ }
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "ss_index_search(%p) %i\n", (void*)or, rc, 0);
+ return rc;
+}
+
+static int
+ss_indexable (struct berval** values)
+ /* at least one of the values is long enough to index */
+{
+ auto struct berval** val = values;
+ if (val) for (; *val; ++val) {
+ auto struct berval value;
+ value.bv_val = (*val)->bv_val;
+ value.bv_len = (*val)->bv_len;
+ if (val == values) { /* initial */
+ if (long_enough (&value, SS_INDEX_LENGTH-1)) return 1;
+ } else if (val[1]) { /* middle */
+ if (long_enough (&value, SS_INDEX_LENGTH)) return 1;
+ } else { /* final */
+ if (long_enough (&value, SS_INDEX_LENGTH-1)) return 1;
+ }
+ }
+ return 0;
+}
+
+static struct berval ss_one_berval = {0,0};
+static struct berval* ss_one_value[2] = {&ss_one_berval, NULL};
+
+static int
+or_filter_index (Slapi_PBlock* pb)
+ /* Return an indexer and values that accelerate the given filter. */
+{
+ auto or_filter_t* or = or_filter_get (pb);
+ auto int rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ auto IFP mrINDEX_FN = NULL;
+ auto struct berval** mrVALUES = NULL;
+ auto char* mrOID = NULL;
+ auto int mrQUERY_OPERATOR;
+ if (or && or->or_indexer && or->or_indexer->ix_index) {
+ switch (or->or_op) {
+ case SLAPI_OP_LESS:
+ case SLAPI_OP_LESS_OR_EQUAL:
+ case SLAPI_OP_EQUAL:
+ case SLAPI_OP_GREATER_OR_EQUAL:
+ case SLAPI_OP_GREATER:
+ mrINDEX_FN = op_index_search;
+ mrVALUES = or->or_values;
+ mrOID = or->or_indexer->ix_oid;
+ mrQUERY_OPERATOR = or->or_op;
+ break;
+ case SLAPI_OP_SUBSTRING:
+ if (ss_indexable (or->or_values)) {
+ if (or->or_oid == NULL) {
+ auto const size_t len = strlen (or->or_indexer->ix_oid);
+ or->or_oid = slapi_ch_malloc (len + 3);
+ memcpy (or->or_oid, or->or_indexer->ix_oid, len);
+ sprintf (or->or_oid + len, ".%1i", SLAPI_OP_SUBSTRING);
+ }
+ mrINDEX_FN = ss_index_search;
+ mrVALUES = ss_one_value;
+ mrOID = or->or_oid;
+ mrQUERY_OPERATOR = SLAPI_OP_EQUAL;
+ }
+ break;
+ default: /* unsupported operator */
+ break;
+ }
+ }
+ if (mrINDEX_FN != NULL &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_OBJECT, or)) &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_TYPE, or->or_type)) &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_INDEX_FN, (void*)mrINDEX_FN)) &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_VALUES, mrVALUES)) &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_OID, mrOID))) {
+ rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_QUERY_OPERATOR, &mrQUERY_OPERATOR);
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "or_filter_index(%p) %i\n",
+ (void*)(or ? or->or_indexer : NULL), rc, 0);
+ return rc;
+}
+
+static int
+or_indexer_create (Slapi_PBlock* pb)
+{
+ auto int rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION; /* failed to initialize */
+ auto char* mrOID = NULL;
+ auto void* mrOBJECT = NULL;
+ if (slapi_pblock_get (pb, SLAPI_PLUGIN_MR_OID, &mrOID) || mrOID == NULL) {
+ LDAPDebug (LDAP_DEBUG_FILTER, "=> or_indexer_create: no OID parameter\n", 0, 0, 0);
+ } else {
+ auto indexer_t* ix = indexer_create (mrOID);
+ auto char* mrTYPE = NULL;
+ slapi_pblock_get (pb, SLAPI_PLUGIN_MR_TYPE, &mrTYPE);
+ LDAPDebug (LDAP_DEBUG_FILTER, "=> or_indexer_create(oid %s; type %s)\n",
+ mrOID, mrTYPE ? mrTYPE : "<NULL>", 0);
+ if (ix != NULL) {
+ if (ix->ix_index != NULL &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_OBJECT, ix) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_MR_OID, ix->ix_oid) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_MR_INDEX_FN, (void*)op_index_entry) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_DESTROY_FN, (void*)op_indexer_destroy)) {
+ mrOBJECT = ix;
+ rc = 0; /* success */
+ } else {
+ indexer_free (ix);
+ }
+ } else { /* mrOID does not identify an ordering rule. */
+ /* Is it an ordering rule OID with the substring suffix? */
+ auto size_t oidlen = strlen (mrOID);
+ if (oidlen > 2 && mrOID[oidlen-2] == '.' &&
+ atoi (mrOID + oidlen - 1) == SLAPI_OP_SUBSTRING) {
+ auto char* or_oid = slapi_ch_strdup (mrOID);
+ or_oid [oidlen-2] = '\0';
+ ix = indexer_create (or_oid);
+ if (ix != NULL) {
+ auto ss_indexer_t* ss = (ss_indexer_t*) slapi_ch_malloc (sizeof (ss_indexer_t));
+ ss->ss_indexer = ix;
+ oidlen = strlen (ix->ix_oid);
+ ss->ss_oid = slapi_ch_malloc (oidlen + 3);
+ memcpy (ss->ss_oid, ix->ix_oid, oidlen);
+ sprintf (ss->ss_oid + oidlen, ".%1i", SLAPI_OP_SUBSTRING);
+ if (ix->ix_index != NULL &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_OBJECT, ss) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_MR_OID, ss->ss_oid) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_MR_INDEX_FN, (void*)ss_index_entry) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_DESTROY_FN, (void*)ss_indexer_destroy)) {
+ mrOBJECT = ss;
+ rc = 0; /* success */
+ } else {
+ ss_indexer_free (ss);
+ }
+ }
+ slapi_ch_free((void**)&or_oid);
+ }
+ }
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "<= or_indexer_create(%p) %i\n", mrOBJECT, rc, 0);
+ return rc;
+}
+
+static Slapi_PluginDesc pdesc = { "orderingrule", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "internationalized ordering rule plugin" };
+
+#define SLAPI_ORPLUGIN_NAME pdesc.spd_description
+
+int /* LDAP error code */
+orderingRule_init (Slapi_PBlock* pb)
+{
+ int rc;
+ int argc;
+ char** argv;
+ char* cfgpath;
+
+/* if (!(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_PRIVATE, ...)) &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_CLOSE_FN, ...)))
+*/
+
+#ifdef USE_HPUX_CC
+ /* not needed with ICU
+ shl_load ( "../lib/libnsbrk30.sl", BIND_IMMEDIATE, 0L );
+ shl_load ( "../lib/libnscnv30.sl", BIND_IMMEDIATE, 0L );
+ shl_load ( "../lib/libnscol30.sl", BIND_IMMEDIATE, 0L );
+ shl_load ( "../lib/libnsfmt30.sl", BIND_IMMEDIATE, 0L );
+ shl_load ( "../lib/libnsres30.sl", BIND_IMMEDIATE, 0L );
+ shl_load ( "../lib/libnsuni30.sl", BIND_IMMEDIATE, 0L );
+ */
+#endif
+
+ if ( slapi_pblock_get( pb, SLAPI_CONFIG_DIRECTORY, &cfgpath ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_ORPLUGIN_NAME,
+ "Unable to retrieve slapd configuration pathname; using default\n" );
+ cfgpath = NULL;
+ }
+
+ collation_init( cfgpath );
+ if (!slapi_pblock_get (pb, SLAPI_PLUGIN_ARGC, &argc) &&
+ !slapi_pblock_get (pb, SLAPI_PLUGIN_ARGV, &argv) &&
+ argc > 0) {
+ collation_read_config (argv[0]);
+ }
+ {
+ slapi_pblock_set (pb, SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, (void*)or_indexer_create);
+ rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_FILTER_CREATE_FN, (void*)or_filter_create);
+ }
+ if ( rc == 0 ) {
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&pdesc );
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "orderingRule_init %i\n", rc, 0, 0);
+ return rc;
+}
diff --git a/ldap/servers/plugins/collation/orfilter.h b/ldap/servers/plugins/collation/orfilter.h
new file mode 100644
index 00000000..85a32592
--- /dev/null
+++ b/ldap/servers/plugins/collation/orfilter.h
@@ -0,0 +1,10 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef _ORFILTER_H_
+#define _ORFILTER_H_
+
+#endif