From 23b61265b85f321ae2ceddc6c75771f8881566d8 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Fri, 5 Sep 2008 10:13:10 -0400 Subject: Initial Import --- ldb/common/attrib_handlers.c | 378 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 378 insertions(+) create mode 100644 ldb/common/attrib_handlers.c (limited to 'ldb/common/attrib_handlers.c') diff --git a/ldb/common/attrib_handlers.c b/ldb/common/attrib_handlers.c new file mode 100644 index 000000000..fb57e2dad --- /dev/null +++ b/ldb/common/attrib_handlers.c @@ -0,0 +1,378 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2005 + + ** NOTE! The following LGPL license applies to the ldb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ +/* + attribute handlers for well known attribute types, selected by syntax OID + see rfc2252 +*/ + +#include "ldb_includes.h" +#include "system/locale.h" +#include "ldb_handlers.h" + +/* + default handler that just copies a ldb_val. +*/ +int ldb_handler_copy(struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *in, struct ldb_val *out) +{ + *out = ldb_val_dup(mem_ctx, in); + if (in->length > 0 && out->data == NULL) { + ldb_oom(ldb); + return -1; + } + return 0; +} + +/* + a case folding copy handler, removing leading and trailing spaces and + multiple internal spaces + + We exploit the fact that utf8 never uses the space octet except for + the space itself +*/ +int ldb_handler_fold(struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *in, struct ldb_val *out) +{ + char *s, *t; + int l; + + if (!in || !out || !(in->data)) { + return -1; + } + + out->data = (uint8_t *)ldb_casefold(ldb, mem_ctx, (const char *)(in->data), in->length); + if (out->data == NULL) { + ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_handler_fold: unable to casefold string [%s]", in->data); + return -1; + } + + s = (char *)(out->data); + + /* remove trailing spaces if any */ + l = strlen(s); + while (l > 0 && s[l - 1] == ' ') l--; + s[l] = '\0'; + + /* remove leading spaces if any */ + if (*s == ' ') { + for (t = s; *s == ' '; s++) ; + + /* remove leading spaces by moving down the string */ + memmove(t, s, l); + + s = t; + } + + /* check middle spaces */ + while ((t = strchr(s, ' ')) != NULL) { + for (s = t; *s == ' '; s++) ; + + if ((s - t) > 1) { + l = strlen(s); + + /* remove all spaces but one by moving down the string */ + memmove(t + 1, s, l); + } + } + + out->length = strlen((char *)out->data); + return 0; +} + + + +/* + canonicalise a ldap Integer + rfc2252 specifies it should be in decimal form +*/ +int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *in, struct ldb_val *out) +{ + char *end; + long long i = strtoll((char *)in->data, &end, 0); + if (*end != 0) { + return -1; + } + out->data = (uint8_t *)talloc_asprintf(mem_ctx, "%lld", i); + if (out->data == NULL) { + return -1; + } + out->length = strlen((char *)out->data); + return 0; +} + +/* + compare two Integers +*/ +int ldb_comparison_Integer(struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *v1, const struct ldb_val *v2) +{ + return strtoll((char *)v1->data, NULL, 0) - strtoll((char *)v2->data, NULL, 0); +} + +/* + compare two binary blobs +*/ +int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *v1, const struct ldb_val *v2) +{ + if (v1->length != v2->length) { + return v1->length - v2->length; + } + return memcmp(v1->data, v2->data, v1->length); +} + +/* + compare two case insensitive strings, ignoring multiple whitespaces + and leading and trailing whitespaces + see rfc2252 section 8.1 + + try to optimize for the ascii case, + but if we find out an utf8 codepoint revert to slower but correct function +*/ +int ldb_comparison_fold(struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *v1, const struct ldb_val *v2) +{ + const char *s1=(const char *)v1->data, *s2=(const char *)v2->data; + size_t n1 = v1->length, n2 = v2->length; + const char *u1, *u2; + char *b1, *b2; + int ret; + while (*s1 == ' ' && n1) { s1++; n1--; }; + while (*s2 == ' ' && n2) { s2++; n2--; }; + /* TODO: make utf8 safe, possibly with helper function from application */ + while (*s1 && *s2 && n1 && n2) { + /* the first 127 (0x7F) chars are ascii and utf8 guarantes they + * never appear in multibyte sequences */ + if (((unsigned char)s1[0]) & 0x80) goto utf8str; + if (((unsigned char)s2[0]) & 0x80) goto utf8str; + if (toupper((unsigned char)*s1) != toupper((unsigned char)*s2)) + break; + if (*s1 == ' ') { + while (s1[0] == s1[1] && n1) { s1++; n1--; } + while (s2[0] == s2[1] && n2) { s2++; n2--; } + } + s1++; s2++; + n1--; n2--; + } + if (! (*s1 && *s2)) { + /* check for trailing spaces only if one of the pointers + * has reached the end of the strings otherwise we + * can mistakenly match. + * ex. "domain users" <-> "domainUpdates" + */ + while (*s1 == ' ') { s1++; n1--; } + while (*s2 == ' ') { s2++; n2--; } + } + if (n1 != n2) { + return n1 - n2; + } + return (int)(toupper(*s1)) - (int)(toupper(*s2)); + +utf8str: + /* no need to recheck from the start, just from the first utf8 char found */ + b1 = ldb_casefold(ldb, mem_ctx, s1, n1); + b2 = ldb_casefold(ldb, mem_ctx, s2, n2); + + if (b1 && b2) { + /* Both strings converted correctly */ + + u1 = b1; + u2 = b2; + } else { + /* One of the strings was not UTF8, so we have no options but to do a binary compare */ + + u1 = s1; + u2 = s2; + } + + while (*u1 & *u2) { + if (*u1 != *u2) + break; + if (*u1 == ' ') { + while (u1[0] == u1[1]) u1++; + while (u2[0] == u2[1]) u2++; + } + u1++; u2++; + } + if (! (*u1 && *u2)) { + while (*u1 == ' ') u1++; + while (*u2 == ' ') u2++; + } + ret = (int)(*u1 - *u2); + + talloc_free(b1); + talloc_free(b2); + + return ret; +} + + +/* + canonicalise a attribute in DN format +*/ +int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *in, struct ldb_val *out) +{ + struct ldb_dn *dn; + int ret = -1; + + out->length = 0; + out->data = NULL; + + dn = ldb_dn_new(ldb, mem_ctx, (char *)in->data); + if ( ! ldb_dn_validate(dn)) { + return LDB_ERR_INVALID_DN_SYNTAX; + } + + out->data = (uint8_t *)ldb_dn_alloc_casefold(mem_ctx, dn); + if (out->data == NULL) { + goto done; + } + out->length = strlen((char *)out->data); + + ret = 0; + +done: + talloc_free(dn); + + return ret; +} + +/* + compare two dns +*/ +int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *v1, const struct ldb_val *v2) +{ + struct ldb_dn *dn1 = NULL, *dn2 = NULL; + int ret; + + dn1 = ldb_dn_new(ldb, mem_ctx, (char *)v1->data); + if ( ! ldb_dn_validate(dn1)) return -1; + + dn2 = ldb_dn_new(ldb, mem_ctx, (char *)v2->data); + if ( ! ldb_dn_validate(dn2)) { + talloc_free(dn1); + return -1; + } + + ret = ldb_dn_compare(dn1, dn2); + + talloc_free(dn1); + talloc_free(dn2); + return ret; +} + +/* + compare two utc time values. 1 second resolution +*/ +int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *v1, const struct ldb_val *v2) +{ + time_t t1, t2; + t1 = ldb_string_to_time((char *)v1->data); + t2 = ldb_string_to_time((char *)v2->data); + return (int)t2 - (int)t1; +} + +/* + canonicalise a utc time +*/ +int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *in, struct ldb_val *out) +{ + time_t t = ldb_string_to_time((char *)in->data); + out->data = (uint8_t *)ldb_timestring(mem_ctx, t); + if (out->data == NULL) { + return -1; + } + out->length = strlen((char *)out->data); + return 0; +} + +/* + table of standard attribute handlers +*/ +static const struct ldb_schema_syntax ldb_standard_syntaxes[] = { + { + .name = LDB_SYNTAX_INTEGER, + .ldif_read_fn = ldb_handler_copy, + .ldif_write_fn = ldb_handler_copy, + .canonicalise_fn = ldb_canonicalise_Integer, + .comparison_fn = ldb_comparison_Integer + }, + { + .name = LDB_SYNTAX_OCTET_STRING, + .ldif_read_fn = ldb_handler_copy, + .ldif_write_fn = ldb_handler_copy, + .canonicalise_fn = ldb_handler_copy, + .comparison_fn = ldb_comparison_binary + }, + { + .name = LDB_SYNTAX_DIRECTORY_STRING, + .ldif_read_fn = ldb_handler_copy, + .ldif_write_fn = ldb_handler_copy, + .canonicalise_fn = ldb_handler_fold, + .comparison_fn = ldb_comparison_fold + }, + { + .name = LDB_SYNTAX_DN, + .ldif_read_fn = ldb_handler_copy, + .ldif_write_fn = ldb_handler_copy, + .canonicalise_fn = ldb_canonicalise_dn, + .comparison_fn = ldb_comparison_dn + }, + { + .name = LDB_SYNTAX_OBJECTCLASS, + .ldif_read_fn = ldb_handler_copy, + .ldif_write_fn = ldb_handler_copy, + .canonicalise_fn = ldb_handler_fold, + .comparison_fn = ldb_comparison_fold + }, + { + .name = LDB_SYNTAX_UTC_TIME, + .ldif_read_fn = ldb_handler_copy, + .ldif_write_fn = ldb_handler_copy, + .canonicalise_fn = ldb_canonicalise_utctime, + .comparison_fn = ldb_comparison_utctime + } +}; + + +/* + return the attribute handlers for a given syntax name +*/ +const struct ldb_schema_syntax *ldb_standard_syntax_by_name(struct ldb_context *ldb, + const char *syntax) +{ + int i; + unsigned num_handlers = sizeof(ldb_standard_syntaxes)/sizeof(ldb_standard_syntaxes[0]); + /* TODO: should be replaced with a binary search */ + for (i=0;i