diff options
Diffstat (limited to 'ldap/servers/plugins/collation')
-rw-r--r-- | ldap/servers/plugins/collation/Makefile | 99 | ||||
-rw-r--r-- | ldap/servers/plugins/collation/collate.c | 454 | ||||
-rw-r--r-- | ldap/servers/plugins/collation/collate.h | 36 | ||||
-rw-r--r-- | ldap/servers/plugins/collation/collation.def | 10 | ||||
-rw-r--r-- | ldap/servers/plugins/collation/config.c | 178 | ||||
-rw-r--r-- | ldap/servers/plugins/collation/config.h | 12 | ||||
-rw-r--r-- | ldap/servers/plugins/collation/debug.c | 14 | ||||
-rw-r--r-- | ldap/servers/plugins/collation/dllmain.c | 129 | ||||
-rw-r--r-- | ldap/servers/plugins/collation/orfilter.c | 984 | ||||
-rw-r--r-- | ldap/servers/plugins/collation/orfilter.h | 10 |
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 |